Friday, December 30, 2011

Access to private members: Safer nastiness.

In one of my previous posts I showed some code that accesses a private member, that now has been posted on the boost mailing list as a possible utility for boost serialization to access private attributes (OH MY, I had no idea such a thing actually could have uses!).

I realized that it would be beneficial to have an alternative implementation because the previous code suffered from the Static Initialization Order Fiasco. The following code does not suffer from that problem anymore:

template<typename Tag, typename Tag::type M>
struct Rob { 
  friend typename Tag::type get(Tag) {
    return M;
  }
};
This defines a friend function that can be called by ADL using the tag type.
// use
struct A {
  A(int a):a(a) { }
private:
  int a;
};

// tag used to access A::a
struct A_f { 
  typedef int A::*type;
  friend type get(A_f);
};

template struct Rob<A_f, &A::a>;

int main() {
  A a(42);
  std::cout << "proof: " << a.*get(A_f()) << std::endl;
}
Note that the explicit declaration of "get" inside of "A_f" is important, because we want "get" to be visible to ADL, and the rob template specialization is not an associated class of "A_f" (we could have declared it globally too, if we wanted, but then it would be visible for anyone). To overcome that, we could introduce some utility class we derive from:
template<typename Tag, typename Member>
struct TagBase {
  typedef Member type;
  friend type get(Tag);
};

The definition of a Tag then becomes very simple
struct A_f : TagBase<A_f, int A::*> { };
Unfortunately with the TagBase, GCC spits out a warning about the "friend type get(Tag)" that it declares a non-template function. The warning can be ignored, but it's annoying :).

Hope you enjoyed!

6 comments:

Anonymous said...

error C2059: syntax error : '<'

Anonymous said...

Ok, man, dont worry I fugured out that I had to include the upmost code as well, and the error dissapeared. So far so good.

Anonymous said...

great idea!

I try use your idom,but got another problem,how to dedue type of member A::a,so i can use marco to do that job.
Like this:
class A
{
int a,b,c;
};
DEF(A,(a)(b)(c))

Atul anand said...
This comment has been removed by the author.
Atul anand said...

#include
using namespace std;
class a{

private:
int a_val;
public:
a():a_val(20){}

};

class a_f{
public:
int b;
};

int main()
{
a temp;
a_f *temp2;
temp2=(a_f *)&temp;
cout<<"\naceesing private a->a_val = "<<(temp2)->b;
cout<<"\n\n";
return 0;
}

Grzegorz said...

Does it works with member functions?

Because the code below:
struct A
{
private:
void fff()
{
cout << "fff called\n";
}
};

template
struct Rob
{
friend typename Tag::type get(Tag)
{
return M;
}
};

struct A_fun
{
typedef void(A::*type)();
friend type get(A_fun);
};

template struct Rob;

int main()
{
A a;
a.*get(A_fun());
}


has compile error:
error: invalid use of non-static member function