Получить виртуальную таблицу класса без объекта

Я пытаюсь реализовать систему, аналогичную первой, описанной здесь. То есть (ab) использование модификации vtable для изменения поведения объекта во время выполнения. Это часть моих попыток создать эффективную оболочку общего типа в проекте C++, над которым я работаю.

Например, если вы не можете получить к нему доступ, скопируйте виртуальную таблицу, используя memcpy() и this указатель как таковой:

void setType( const DataType& newType )
{
    memcpy( this, &newType, sizeof(DataType) );
}

Однако у меня есть проблема с этим методом: у меня нет объекта целевого класса для копирования виртуальной таблицы, и я не хочу создавать его, так как некоторые типы являются дорогостоящими для создания.

Есть ли способ получить доступ к vtable, который был бы включен в объект данного класса без объекта этого класса?

Было бы предпочтительнее, если бы он был несколько переносимым, но я в значительной степени смирился с тем, что это зависит от компилятора; как таковой, метод только GCC/G++ будет приемлемым, если нет другой опции. Предположим также, что меня интересует только построение этого на достаточно стандартных ОС и архитектурах.

Я использую C++11, если это поможет как-то.

Редактировать: Я хочу быть полностью ясным, я знаю, насколько опасно такое поведение. Меня больше интересует идея и, возможно, ее узкое применение в очень контролируемых обстоятельствах, чем то, что я являюсь хорошей идеей для производственного программного обеспечения, несмотря на то, что может предложить мое вступление.

1 ответ

Решение
struct many_vtable {
  void(*dtor)(void*);
  void(*print)(void const*,std::ostream&);
};
template<class T>struct tag_t{};
template<class T>constexpr tag_t<T> tag = {};

template<class T>
many_vtable const* make_many_vtable(tag_t<T>){
  static const many_vtable retval = {
    // dtor
    [](void* p){
      reinterpret_cast<T*>(p)->~T();
    },
    // print
    [](void const*p, std::ostream& os){
      os<<*reinterpret_cast<T const*>(p);
    }
  };
  return &retval;
}
struct many {
  many_vtable const* vtable=nullptr;
  std::aligned_storage_t<100, alignof(double)> buff;
  void clear(){if(vtable) vtable->dtor(&buff);vtable=nullptr;}
  ~many(){ clear(); }
  many()=default;
  many(many const&)=delete; // not yet supported
  many& operator=(many const&)=delete; // not yet supported

  explicit operator bool()const{return vtable!=nullptr;}

  template<class T,class...Args>
  void emplace(Args&&...args){
    static_assert(alignof(T) <= alignof(double), "not enough alignment");
    static_assert(sizeof(T) <= 100, "not enough size");
    clear();
    ::new((void*)&buff) T(std::forward<Args>(args)...);
    vtable=make_many_vtable(tag<T>);
  }
  friend std::ostream& operator<<(std::ostream& os, many const&m){
    if(!m.vtable) return os;
    m.vtable->print(&m.buff, os);
    return os;
  }
};

Это ручной дизайн Vtable. Он может хранить все до 100 байтов, чье выравнивание меньше двойного, которое может быть напечатано в поток.

"Стирание" до нескольких (или разных) операций легко. Например, возможность скопировать / переместить в другое множество.

Это не нарушает стандарт, и у него есть издержки, аналогичные связанному примеру.

many m;
m.emplace<int>(3);
std::cout << m << '\n';
m.emplace<double>(3.14);
std::cout << m << '\n';

живой пример.

Это vtables вручную, потому что мы в основном реализуем концепцию vtable вручную.

Другие вопросы по тегам