Получить vtable класса без объекта

101
14

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


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


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

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


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


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


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


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

спросил(а) 2021-01-19T16:47:12+03:00 6 месяцев, 1 неделя назад
1
Решение
89

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 вручную.

ответил(а) 2021-01-19T16:47:12+03:00 6 месяцев, 1 неделя назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема