获取没有对象的类的vtable
Posted
技术标签:
【中文标题】获取没有对象的类的vtable【英文标题】:Obtain the vtable of a class without an object 【发布时间】:2016-03-04 16:48:49 【问题描述】:我正在尝试实现类似于the first described here 的系统。也就是说,(ab)使用 vtable 修改来改变运行时的对象行为。这是我尝试在我正在处理的 C++ 项目中创建高效类型泛型包装器的一部分。
如果您无法访问该示例,请使用 memcpy()
和 this
指针复制 vtable:
void setType( const DataType& newType )
memcpy( this, &newType, sizeof(DataType) );
但是,我对这种方法有一个问题:我没有目标类的对象来复制 vtable,并且不想创建一个对象,因为某些类型的构建成本很高。
有没有办法访问 vtable,该 vtable 将被放置到给定类的对象中,而没有该类的对象?
如果它具有一定的可移植性会更好,但我基本上已经接受了编译器特定的;因此,如果没有其他选择,只能接受 GCC/G++ 方法。我们还假设我只关心在相当标准的操作系统和架构上构建它。
我正在使用 C++11,如果这有助于解决这个问题。
编辑:我想完全清楚,我知道这种行为有多危险。尽管我的介绍可能会建议,但我对这个想法以及它在非常受控的环境中的狭窄应用更感兴趣,而不是我对它作为生产软件的好想法。
【问题讨论】:
当然你需要一个对象的实例,那么为什么不在它被创建之后修改它呢? [虽然修改 vtables 似乎是一个糟糕的主意,它甚至可能不存储在可写内存中,而且现在的编译器确实会跟踪对象并跳过 vtable 访问,如果它“知道”要调用什么] 如果你不想在运行时改变对象实例的行为,而是实现State Design Pattern。你想要的东西不可能以可移植的方式实现,编译器不需要根据 c++ 标准使用 vtable 来实现动态多态性。 两个想法:(1) PIMPL,您可以在其中创建一个没有 impl 东西的对象来获取 vtable(为此使用一些特殊的 ctor)。这将使为 vtable 创建一个“虚拟”对象变得轻量,而不是实际使用。 (2) 实现一个具有局部静态变量的模板化工厂函数,持有一个虚拟实例(一种单例)只是为了获取 vtable。所以你总共只有一个类型的对象作为你的开销,我想这是可以的。 (1+2) 甚至可以组合,但我想没必要。 @MatsPetersson 我知道这是建立在非标准行为之上的,因此我指出这是对 vtables 的“滥用”。我正在尝试评估,除了我提出的这个问题之外,它是否是我感兴趣的平台上的一个可行概念。非常可能的问题并没有真正影响我的问题。 - 至于必须有一个实例,不,我没有:例如,我有一个子类来包装一个整数,但我从不创建该子类的实例。我只是在将整数放入包装器时放置它的 vtable。实际上,每次设置时,我都会将基类转变为继承类。 在链接代码中,对象只不过是它的 vtable。其次,考虑安置新的和歧视性的工会。 【参考方案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()constreturn 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 个字节的对齐方式小于可以打印到流的 double 的任何内容。
“擦除”到更多(或不同)操作很容易。例如,能够复制/移动到另一个许多。
它不违反标准,并且与链接示例具有相似的开销。
many m;
m.emplace<int>(3);
std::cout << m << '\n';
m.emplace<double>(3.14);
std::cout << m << '\n';
live example.
这是手动 vtable,因为我们基本上是手动重新实现 vtable 概念。
【讨论】:
虽然不是对我提出的问题的直接回答,但这对我陈述的用例来说似乎比我正在调查的更有用。因此,我会接受它作为答案。感谢您编写示例。以上是关于获取没有对象的类的vtable的主要内容,如果未能解决你的问题,请参考以下文章