Я знаю, что это старый вопрос, но проблема по-прежнему актуальна. В то время как мне нравится идея идиома «адвокат-клиент», я хотел прозрачный интерфейс для клиентских классов, которым был предоставлен частный (или защищенный) доступ.
Я думаю, что нечто подобное уже было сделано, но беглый взгляд вокруг ничего не дал. Следующий метод (C ++ 11 и выше) работает для каждого класса (не для объекта) и использует базовый класс CRTP, который используется «закрытым классом» для представления открытого функтора. Только те классы, которым конкретно предоставлен доступ, могут вызывать оператор функтора (), который затем напрямую вызывает связанный закрытый метод через сохраненную ссылку.
Нет никаких накладных расходов на вызовы функций, и единственные накладные расходы памяти - это одна ссылка на частный метод, который требует раскрытия. Система очень универсальна; любая сигнатура функции и возвращаемый тип допускаются так же, как и вызов виртуальных функций в закрытом классе.
Для меня главное преимущество - это синтаксис. Хотя в приватном классе требуется довольно уродливое объявление объектов-функторов, оно полностью прозрачно для клиентских классов. Вот пример, взятый из исходного вопроса:
struct Doctor; struct Judge; struct TaxMan; struct TheState;
struct Medicine {} meds;
class Person : private GranularPrivacy<Person>
{
private:
int32_t money_;
void _takePill (Medicine *meds) {std::cout << "yum..."<<std::endl;}
std::string _tellTruth () {return "will do";}
int32_t _payDollars (uint32_t amount) {money_ -= amount; return money_;}
public:
Person () : takePill (*this), tellTruth (*this), payDollars(*this) {}
Signature <void, Medicine *>
::Function <&Person::_takePill>
::Allow <Doctor, TheState> takePill;
Signature <std::string>
::Function <&Person::_tellTruth>
::Allow <Judge, TheState> tellTruth;
Signature <int32_t, uint32_t>
::Function <&Person::_payDollars>
::Allow <TaxMan, TheState> payDollars;
};
struct Doctor
{
Doctor (Person &patient)
{
patient.takePill(&meds);
// std::cout << patient.tellTruth(); //Not allowed
}
};
struct Judge
{
Judge (Person &defendant)
{
// defendant.payDollars (20); //Not allowed
std::cout << defendant.tellTruth() <<std::endl;
}
};
struct TheState
{
TheState (Person &citizen) //Can access everything!
{
citizen.takePill(&meds);
std::cout << citizen.tellTruth()<<std::endl;
citizen.payDollars(50000);
};
};
Базовый класс GranularPrivacy работает путем определения 3 вложенных шаблонных классов. Первый из них, «Подпись», принимает тип возвращаемого значения функции и сигнатуру функции в качестве параметров шаблона и перенаправляет их как в метод operator () функтора, так и во второй класс шаблона гнезда, «Функция». Это параметризуется указателем на приватную функцию-член класса Host, которая должна иметь подпись, предоставляемую классом Signature. На практике используются два отдельных класса «Function»; один приведенный здесь, а другой для константных функций, для краткости опущен.
Наконец, класс Allow рекурсивно наследует от явно инстанцированного базового класса, используя механизм шаблонов с переменными числами, в зависимости от количества классов, указанных в его списке аргументов шаблона. У каждого уровня наследования Allow есть один друг из списка шаблонов, а операторы using выводят конструктор базового класса и operator () вверх по иерархии наследования в наиболее производную область.
template <class Host> class GranularPrivacy
{
friend Host;
template <typename ReturnType, typename ...Args> class Signature
{
friend Host;
typedef ReturnType (Host::*FunctionPtr) (Args... args);
template <FunctionPtr function> class Function
{
friend Host;
template <class ...Friends> class Allow
{
Host &host_;
protected:
Allow (Host &host) : host_ (host) {}
ReturnType operator () (Args... args) {return (host_.*function)(args...);}
};
template <class Friend, class ...Friends>
class Allow <Friend, Friends...> : public Allow <Friends...>
{
friend Friend;
friend Host;
protected:
using Allow <Friends...>::Allow;
using Allow <Friends...>::operator ();
};
};
};
};
Я надеюсь, что кто-то найдет это полезным, любые комментарии или предложения будут приветствоваться. Это определенно все еще в стадии разработки - я бы особенно хотел объединить классы Signature и Function в один шаблонный класс, но изо всех сил пытался найти способ сделать это. Более полные, выполнимые примеры можно найти по cpp.sh / 6ev45 и cpp.sh / 2rtrj .