Saturday, April 24, 2010

private_ptr: A friend smart pointer to access private members

Friend can be used to access the internals of a class for unitary test purposes. We can define an smart pointer private_ptr template class and a private_cast template function, which gives a private access to a class in the same way const_cast grant non const access.
// private_ptr.h
template <typename Concept> 
class private_ptr {
  private_ptr(Concept & c) : c_(c) {}
  Concept * operator->() {
    return &c_;
  }
  const Concept* operator->() const {
    return &c_;
  }
};
template <typename Concept> 
private_ptr> private_cast(Concept& c) {
    return private_ptr(c);
}
A class can grant friend access to the private pointer class in a controlled way,  e.g. unit tests access to the private members,  as follows:

#include <private_ptr.hpp>
class C {
#ifdef C_UNIT_TEST
    friend class private_ptr<C>;
#endif
    // ...
};
As the friend class is defined only if C_UNIT_TEST is defined, there is no risk that application code uses this back-door access. The unit test code can access private members as follows:

#include <C.hpp>
void test_private()
{
  // ...
  C c;
  private_cast(c)->private_op();
  assert(private_cast(c)->private_field==0);
  //...
}

Unfortunately this smart pointer can not be used to access private types.

3 comments:

  1. Hello Vicente, that is only an "idea" or there is a way to do that?

    I thought that making a class inheriting private_ptr would solve the problem but I was wrong.

    I thought if something is visible to private_ptr is visible to the classes who inherits private_ptr. But I thought better and saw that private members of private_ptr are not visible to its 'childs'.

    So I tried to declare the private_ptr as friend of it child: did not work.

    Murilo.

    ReplyDelete
  2. Hi Murillo,
    unfortunately friendship is not transitive :(

    ReplyDelete
  3. HI Vicente,
    Test can get full access to private members by using "test probe":
    http://stackoverflow.com/a/17526024/834552

    Class that wants to open private members for a test case has to declare template class:

    class SystemUnderTest
    {
    //...put this 'Tested' declaration into private area of a class that you are going to test
    template class Tested;
    ...
    };

    ReplyDelete