2016-03-04 7 views
4

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

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

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

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

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

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

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

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

+0

Несомненно, вам нужен экземпляр объекта В НЕКОТОРЫХ пунктах, так почему бы просто не изменить его ПОСЛЕ того, как он был создан? [Хотя кажется, что ужасная идея в целом изменить vtables, которая может даже не быть сохранена в записываемой памяти, а в наши дни компиляторы отслеживают объекты и пропускают доступ к vtable, если они «знают», что вызывать] –

+0

Если вы wan 't изменить поведение экземпляров объектов во время выполнения, а реализовать [State Design Pattern] (https://sourcemaking.com/design_patterns/state/cpp/1). То, что вы хотите, невозможно в переносном режиме, компилятор не требуется для реализации динамического полиморфизма с использованием виртуальной таблицы при любом использовании стандарта C++. –

+0

Две идеи: (1) PIMPL, где вы создаете объект без материала impl, чтобы получить vtable (используя для этого специальный ctor). Это облегчило бы создание «фиктивного» объекта только для vtable, а не для фактического использования. (2) Реализовать шаблонную фабричную функцию, которая имеет локальную статическую переменную, содержащую фиктивный экземпляр (вид singleton), чтобы получить vtable. Таким образом, у вас есть только один объект каждого типа, как ваш накладные расходы в целом, что, я думаю, все в порядке. (1 + 2) Они могут быть даже объединены, но я думаю, это не обязательно. – leemes

ответ

3
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'; 

live example.

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

+0

Хотя это не прямой ответ на поставленный мной вопрос, это кажется более полезным для моего заявленного варианта использования, чем то, что я изучал. Поэтому я соглашусь на это как на ответ. Спасибо, что написал пример. –