Info
-
Did you know that C++ allows accessing private members with friend injection?
Example
class foo {
private:
int data;
};
template<int foo::*Ptr>
int& get_data(foo& f) {
return f.*Ptr;
}
template<int foo::*Ptr>
struct foo_access {
friend int& get_data(foo& f) {
return f.*Ptr;
}
};
template struct foo_access<&foo::data>;
int& get_data(foo&);
int main() {
foo f{};
get_data(f) = 42; // access private data member
}
Puzzle
- Can you implement
get_token
which gives access to the private trade token?
class trade {
public:
int price{};
int size{};
constexpr auto token() const { return token_; }
private:
std::string_view token_{};
};
// TODO
std::string_view& get_token(trade&);
int main() {
using namespace boost::ut;
"access private token"_test = [] {
trade t{};
expect("" == t.token());
get_token(t) = "Quantlab";
expect("Quantlab" == t.token());
};
}
Solutions
class trade {
friend std::string_view& get_token(trade&);
public:
int price{};
int size{};
constexpr auto token() const { return token_; }
private:
std::string_view token_{};
};
std::string_view& get_token(trade& t) {
return t.token_;
}
class trade {
public:
int price{};
int size{};
constexpr auto token() const { return token_; }
private:
std::string_view token_{};
};
template <std::string_view trade::*Ptr>
struct trade_access {
friend auto get_token(trade& t) -> std::string_view& {
return t.*Ptr;
}
};
template struct trade_access<&trade::token_>;
auto get_token(trade& t) -> std::string_view&;
template<class T, class V, V T::*Ptr, class Token>
struct BadFriend {
friend V& get_data(T& f , Token const & ) {
return f.*Ptr;
}
};
#define ACCESS( TYPE, MEMBER_TYPE, MEMBER ) \
struct MEMBER{ \
MEMBER( TYPE & t ):t(t){} \
auto & operator()(); \
TYPE & t; \
}; \
template struct BadFriend<TYPE, MEMBER_TYPE, &TYPE::MEMBER, MEMBER>; \
MEMBER_TYPE & get_data(TYPE&,MEMBER const & ); \
auto & MEMBER::operator()(){ \
return get_data( t, *this ); \
}
ACCESS(trade,std::string_view,token1_)
ACCESS(trade,std::string_view,token2_)
namespace steal {
template <class>
struct trait;
template <class T, class U>
struct trait<T U::*> { using type = U; };
template<auto ptr, class tag>
struct member_pointer {
friend constexpr auto& get_member_pointer(
typename trait<decltype(ptr)>::type& f, const tag*) noexcept
{ return f.*ptr; }
};
}
// Must not be pasted inside any namespace
#define EXPOSE_MEMBER_AND_THEN_GO_THINK_ABOUT_WHAT_YOU_HAVE_DONE(NS, BASE, MEMBER) \
namespace NS { \
class BASE##_##MEMBER { \
BASE& base; \
public: \
constexpr BASE##_##MEMBER(BASE& base) : base(base) {} \
constexpr auto& operator()() const noexcept; \
}; \
} \
namespace steal { \
template class member_pointer<&::NS::BASE::MEMBER, ::NS::BASE##_##MEMBER>; \
constexpr auto& get_member_pointer( \
::NS::BASE&, const ::NS::BASE##_##MEMBER*) noexcept; \
} \
constexpr auto& ::NS::BASE##_##MEMBER::operator()() const noexcept { \
return ::steal::get_member_pointer(base, this); \
}
namespace business::messages {
class trade {
public:
int price{};
int size{};
constexpr auto token1() const { return token1_; }
constexpr auto token2() const { return token2_; }
private:
std::string_view token1_{};
std::string_view token2_{};
};
}
EXPOSE_MEMBER_AND_THEN_GO_THINK_ABOUT_WHAT_YOU_HAVE_DONE(business::messages, trade, token1_)
EXPOSE_MEMBER_AND_THEN_GO_THINK_ABOUT_WHAT_YOU_HAVE_DONE(business::messages, trade, token2_)