Reputation: 1342
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)?
Upvotes: 1
Views: 406
Reputation: 218323
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);
}
Upvotes: 1
Reputation: 385405
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.
Upvotes: 0