1

I'm trying to access a protected member of a class defined and implemented by an external library. I'd like to achieve that without copying or moving the instance if possible.

Here is a simplified example. Let's say this is the code from the external library:

// some_external_library.h

class Node {
    public:
        static Node Create();
    protected:
        Node(int val) : val_(val) {}
        int val_;
};

This is what I am trying to do:

// my_code.cpp

#include "some_external_library.h"

Node node = Node::Create();
int val = node.val_; // <-- This is invalid since val_ is protected.

Node class is part of some external library that I link to my program. So, I'd like to avoid modifying Node class by adding a public function or friend declaration for accessing val_. Also, I'd like to avoid creating a copy of node object or moving from it, so I'd like to avoid creating a derived class moving/copying the Node instance just to access a member field. Is it possible?

I came up with a possible solution, which worked fine for the minimal example:

// my_code.cpp

#include "some_external_library.h"
class NodeAccessor : public Node {
    public:
        int val() const { return val_; }
};

Node node = Node::Create();
int val = static_cast<const NodeAccessor&>(node).val();

However, I'm not sure if this is valid since node isn't an instance of NodeAccessor. Is this standard compliant? Would it cause any problems (e.g. optimizing away of val_ during compilation of the external library)?

ofo
  • 1,160
  • 10
  • 21
  • No it is not standard compliant. There are no standard-sanctioned backdoors into member access. You are just not allowed to do what you want to do. Full stop, end of story. – n. m. could be an AI Aug 11 '19 at 16:54
  • Why do you think you need to do this? It should be self-evident that you're not supposed to... – Lightness Races in Orbit Aug 11 '19 at 16:57
  • 1
    @n.m.: There is an "allowed" way to access private member, there is a "hole" with explicit template instantiations. – Jarod42 Aug 11 '19 at 17:31
  • 2
    Note that _if_ `val_` is the first data member of `Node`, and _if_ `Node` is a standard-layout type, then [a pointer to Node is exactly identical to a pointer to `Node::val_`](https://timsong-cpp.github.io/cppwp/n4659/basic.compound#4.3), and you're free to `reinterpret_cast` a `Node*` to an `int*`. This may not necessarily be a good idea, though. – Justin Time - Reinstate Monica Aug 11 '19 at 17:42
  • @Jarod42 hmm yes indeed, what a shame. – n. m. could be an AI Aug 12 '19 at 08:47

2 Answers2

1

What you do is UB.

There is one magical way to access private/protected member in C++:

The ISO C++ standard specifies that there is no access check in case of explicit template instantiations, and following code abuse of that:

template <typename Tag>
struct result
{
  using type = typename Tag::type;
  static type ptr;
};

template <typename Tag> typename result<Tag>::type result<Tag>::ptr;

template<typename Tag, typename Tag::type p>
struct rob : result<Tag> {
  /* fill it ... */
  struct filler {
    filler() { result<Tag>::ptr = p; }
  };
  static filler filler_obj;
};

template <typename Tag, typename Tag::type p>
typename rob<Tag, p>::filler rob<Tag, p>::filler_obj;

And then

struct NodeVal { using type = int Node::*; };
template class rob<NodeVal, &Node::val_>;

and finally:

int main() {
  Node node = /**/;
  (node.*result<NodeVal>::ptr);
}

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Thank you very much! Parsing the code will take some time. I had no idea about this exception about explicit template instantiations. More related links for the curious: https://stackoverflow.com/questions/39807325/why-does-a-hole-for-access-checking-exist-for-explicit-template-instantiations https://stackoverflow.com/questions/23920027/why-do-the-usual-access-control-checking-applies-to-names-used-to-specify-explic http://www.gotw.ca/gotw/076.htm – ofo Aug 11 '19 at 17:40
  • Oh god. Can we make it clearer that this is an abuse and the OP _should not do this_? They are violating the third-party interface. Who knows what manner of crazy can happen as a result. _Just don't do it!!_ – Lightness Races in Orbit Aug 11 '19 at 17:41
  • @LightnessRacesinOrbit You are reading too much into a technical question IMHO. If it relieves you any way, cited GotW link has a section at the very end about why it exists and why it isn't considered a good practice. See "3. Is this a hole in C++'s access control mechanism, and therefore a hole in C++'s encapsulation? Discuss." – ofo Aug 11 '19 at 17:48
  • @ofo All I'm reading into it is that you want to do this in real code, and that's worrying, because you should not. – Lightness Races in Orbit Aug 11 '19 at 17:50
0

Yes, that can and will and should cause problems.

You are casting a thing to a type, then the thing is not of that type.

All manner of antics can ensue as a result.

There is simply no way to do what you're trying to do, and that's a good thing! Access protections are there for a reason. Work with them, not against them.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Thanks. I anticipate it isn't valid, but I couldn't find a reason for *why* (beyond opinions). Can you provide some reasoning, preferably citing the reference, or at least pointing towards the related C++ language rule? – ofo Aug 11 '19 at 17:11
  • @ofo I think you're going about this backwards. Why _would_ the language go to all this trouble to implement access control mechanisms, then allow you to completely ignore them? That would defeat their entire purpose! So there is no reasoning or reference or rule or citation - there is only common sense and the fact that no feature exists to do what you want. – Lightness Races in Orbit Aug 11 '19 at 17:15
  • It might help if, again, you explain why you think you want to do this. – Lightness Races in Orbit Aug 11 '19 at 17:16
  • We don't add features because we can't think of an objective reason not to; we add features when we can think of an objective reason to. There is no objective reason to violate the fundamental principles of OO in the manner that you are proposing. – Lightness Races in Orbit Aug 11 '19 at 17:20
  • *"There is simply no way to do what you're trying to do"*. With C++ arcanes, we can have access to (most) private members. – Jarod42 Aug 11 '19 at 17:33