A design pattern not very well known is the PassKey pattern that is used mainly to restrict access to certain public functions (the option would be to use friend in the main classes and that produces too much coupling).
A basic implementation could be:
template<class T>
class PassKey
{
friend T;
PassKey()
{ }
PassKey(PassKey const&)
{ }
PassKey& operator=(PassKey const&) = delete;
};
Note that all the elements are private ... the grace is that only the type T
will be able to create objects of this type. An example of use to see it better:
class Objeto
{
public:
// Puede ser llamada por cualquiera
void FuncionPublica()
{ }
// Solo Autorizado puede llamar a esta funcion
void FuncionRestringida(PassKey<Autorizado>)
{ }
private:
// Nadie puede llamar a esta funcion
void FuncionPrivada()
{ }
};
class Autorizado
{
public:
void Func(Objeto& obj)
{
obj.FuncionPublica(); // ok
obj.FuncionRestringida(PassKey<Autorizado>()); // ok
obj.FuncionPrivada(); // error de compilacion -> esperado
}
};
class Espia
{
public:
void Func(Objeto& obj)
{
obj.FuncionPublica(); // ok
obj.FuncionRestringida(PassKey<Autorizado>()); // error de compilacion
obj.FuncionRestringida(PassKey<Espia>()); // error de compilacion
obj.FuncionPrivada(); // error de compilacion -> esperado
}
};
Well, now that the context is explained, we go to the mess. Now it turns out that Objeto.FuncionRestringida()
has to be accessed by two different classes:
class Objeto
{
public:
// Solo Autorizado y OtraClase pueden llamar a esta funcion
void FuncionRestringida(PassKey<Autorizado,OtraClase>);
};
... in theory the problem does not have too much difficulty ... just enlarge the template. let's see:
version1
The template now supports two types:
template<class T1, class T2 = void>
class PassKey
{
friend T1;
friend T2;
PassKey()
{ }
PassKey(PassKey const&)
{ }
PassKey& operator=(PassKey const&) = delete;
};
Problem ... the copy constructor requires that the received object be of type PassKey<Autorizado,OtraClase>
, which forces to modify both Autorizado
and OtraClase
so that they create the object of the correct type ... too cumbersome:
class Autorizado
{
public:
void Func(Objeto& obj)
{
obj.FuncionRestringida(PassKey<Autorizado,OtraClase>()); // ok pero engorroso..
obj.FuncionRestringida(PassKey<OtraClase,Autorizado>()); // error de compilacion
}
};
The idea should be that each one worries about creating a key with its type and creating some mechanism that makes the relevant conversions:
PassKey<T1> -> PassKey<T1,T2>
PassKey<T2> -> PassKey<T1,T2>
version 2
We tried to overload the builders to make the conversions:
template<class T1, class T2 = void>
class PassKey
{
friend T1;
friend T2;
PassKey()
{ }
PassKey(PassKey const&)
{ }
PassKey(PassKey<T1> const&)
{ }
PassKey(PassKey<T2> const&)
{ }
PassKey& operator=(PassKey const&) = delete;
};
Problem ... compilation errors occur:
error: multiple overloads of 'PassKey' instantiate to the same signature 'void (const PassKey<Autorizado> &)'
PassKey(PassKey<T1> const&)
^
note: in instantiation of template class 'PassKey<Autorizado, void>' requested here
obj.FuncionRestringida(PassKey<Autorizado>());
^
note: previous declaration is here
PassKey(PassKey const&)
^
error: multiple overloads of 'PassKey' instantiate to the same signature 'void (const PassKey<OtraClase> &)'
PassKey(PassKey<T1> const&)
^
note: in instantiation of template class 'PassKey<OtraClase, void>' requested here
obj.FuncionRestringida(PassKey<OtraClase>());
^
note: previous declaration is here
PassKey(PassKey const&)
error: 'PassKey<T1, T2>::PassKey(const PassKey<T2>&) [with T1 = void; T2 = void]' cannot be overloaded
error: with 'PassKey<T1, T2>::PassKey(const PassKey<T1>&) [with T1 = void; T2 = void]'
In member function 'void Autorizado::Func(Objeto&)':
error: initializing argument 1 of 'void Objeto::FuncionRestringida(PassKey<Autorizado>)'
At this point ... is there a solution to the problem?