Возможно ли иметь переменную-член, которая могла бы вычислять указатель на содержащий объект из указателя на себя (в его методе)?
Давайте создадим интерфейс внешнего вызова в API, подобный этому:
template <typename Class, MethodId Id, typename Signature>
class MethodProxy;
template <typename Class, MethodId Id, typename ReturnT, typename Arg1T>
class MethodProxy<Class, Id, ReturnT ()(Arg1T) {
public:
ReturnT operator()(Class &invocant, Arg1T arg1);
};
и аналогично для других чисел аргументов от 0 до N. Для каждого внешнего класса один класс C ++ объявлен с некоторыми чертами, и этот шаблон использует эти черты (и больше черт для аргументатипы), чтобы найти и вызвать чужой метод.Это можно использовать следующим образом:
Foo foo;
MethodProxy<Foo, barId, void ()(int)> bar;
bar(foo, 5);
Теперь я хотел бы определить Foo
таким образом, чтобы я мог назвать так:
Foo foo;
foo.bar(5);
безмногократное повторение подписи. (очевидно, создать статический член и обернуть вызов в метод просто, верно).Ну, на самом деле, это все еще просто:
template <typename Class, MethodId Id, typename Signature>
class MethodMember;
template <typename Class, MethodId Id, typename ReturnT, typename Arg1T>
class MethodMember<Class, Id, ReturnT ()(Arg1T) {
MethodProxy<Class, Id, Signature> method;
Class &owner;
public:
MethodMember(Class &owner) : owner(owner) {}
ReturnT operator()(Arg1T arg1) { return method(owner, arg1); }
};
Это, однако, означает, что объект будет содержать много копий указателя на себя.Поэтому я ищу способ, чтобы эти экземпляры могли рассчитывать указатель владельца по this
и некоторым дополнительным аргументам шаблона.
Я думал так:
template <typename Class, size_t Offset, ...>
class Member {
Class *owner() {
return reinterpret_cast<Class *>(
reinterpret_cast<char *>(this) - Offset);
}
...
};
class Foo {
Member<Foo, offsetof(Foo, member), ...> member;
...
};
но это жалуется, что Foo является неполным типом в данный момент.
Да, я знаю, offsetof
должен работать только для типов "POD", но на практике для любого не виртуального члена, который это будет, работает.Я также попытался передать указатель на (тот) член (используя фиктивный базовый класс) в этом аргументе, но это тоже не сработало.
Обратите внимание, что если это сработало, это также может бытьиспользуется для реализации C # -подобных свойств, делегирующих методы содержащего класса.
Я знаю, как сделать методы-оболочки, упомянутые выше, с boost.preprocessor, но списки аргументов должны были бы быть указаны в странной форме.Я знаю, как написать макрос для генерации универсальных оболочек через шаблоны, но это, вероятно, дало бы плохую диагностику.Также было бы тривиально, если бы звонки выглядели как foo.bar()(5)
.Но я хотел бы знать, возможен ли какой-нибудь умный трюк (плюс только такой умный трюк, вероятно, будет пригоден и для свойств).
Примечание: тип элемента не может быть фактически специализирован на любом указателе элементадля него, ни его смещение, потому что тип должен быть известен, прежде чем это смещение может быть назначено.Это потому, что тип может влиять на требуемое выравнивание (рассмотрим явную / частичную специализацию).