Доступ к родительскому классу из члена без сохранения указателя на родительский - PullRequest
0 голосов
/ 30 мая 2020

Можно ли безопасно получить доступ к родительскому классу из члена без необходимости хранить указатель родительского класса в члене? Что-то вроде этого указателя, но только родительский указатель?

#include <cstddef>
#include <cstdint>

#include <iostream>

template< class Member, class Parent, ptrdiff_t parentToMemberOffset>
struct ParentAccess
{
  Parent & parent() { return *reinterpret_cast<Parent*>(reinterpret_cast<char*>(static_cast<Member*>(this)) - parentToMemberOffset); }
};

template<class Parent, ptrdiff_t parentToMemberOffset>
struct A : ParentAccess<A<Parent,parentToMemberOffset>, Parent, parentToMemberOffset>
{
  void notifyParent() { this->parent().notify(); }
};

struct Host
{
  uint64_t counter = 123;
  A<Host, 8 /*what safe here?*/> a;

  void notify() { counter++; std::cout << "counter is " << counter; }
};

int main()
{
    Host h;
    h.a.notifyParent();
    return 0;
}

https://ideone.com/Nps2Ye

1 Ответ

0 голосов
/ 09 июня 2020

Решение:

#include <cstddef>
#include <cstdint>

#include <iostream>

template< class Member, class Parent, size_t memberId  = 0>
struct ParentAccess
{
  const static ptrdiff_t offset;
  Parent & parent() { return *reinterpret_cast<Parent*>(reinterpret_cast<char*>(static_cast<Member*>(this)) -
    offset ); }
};

template<class Parent, size_t memberId = 0>
struct A : ParentAccess<A<Parent, memberId>, Parent, memberId>
{
  void notifyParent() { this->parent().notify(); }
};

struct Host
{
  enum MemberId { A0, A1 };
  uint64_t counter = 123;
  A<Host/*,A0*/> a;
  A<Host,A1> a1;

  void notify() { counter++; std::cout << "counter is " << counter << std::endl; }
};

// define the offsets
template<>
const ptrdiff_t ParentAccess<A<Host>,Host>::offset = offsetof(Host, a);
//template<>
//const ptrdiff_t ParentAccess<A<Host,Host::A1>,Host,Host::A1>::offset = offsetof(Host, a1); 
// or using a macro to define the offset
#define DEFINE_PARENT_ACCESS_OFFSET(parentClassName, memberClass, memberName, memberId) \
template<>\
const ptrdiff_t ParentAccess<memberClass<parentClassName,parentClassName::memberId>,parentClassName,parentClassName::memberId>::offset = offsetof(parentClassName, memberName) 
DEFINE_PARENT_ACCESS_OFFSET(Host,A,a1,A1);

int main()
{
    Host h;
    h.a.notifyParent();
    h.a1.notifyParent();
    return 0;
}

https://ideone.com/JeZ2lh

Назовем это «Элемент с нулевыми накладными расходами на шаблон указателя экземпляра класса».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...