简短回答:这可能会很困难。
问题很多,但基本上归结为C和C++中可访问的操作非常低级(注意:可访问,但不一定合法)。void*
的作用是任何指针都可以强制转换为它,但如果您应该使用另一种类型,滥用reinterpret_cast
仍然可能会导致麻烦。
一个简单的解决方案是使用标记。基本上,在指针中放置一个类型 id,这样你就可以始终知道对象的原始类型。虽然这很繁琐(因为每种类型都需要修改),但部署起来很容易。
typedef enum {
SP_ATag,
SP_BTag,
SP_CTag,
...
} SP_Tag_t;
typedef struct {
SP_Tag_t tag;
void* p;
} SP_Any_t;
struct A {
SP_Tag_t tag;
...;
};
...
typedef union {
A* a;
B* b;
C* c;
} SP_Any_t;
然后,您可以使用SP_Any_t
代替void*
。
优点:
- 轻量级
- 外部解决方案不需要修改现有类
- 内部解决方案应与现有C代码二进制兼容
缺点:
- 声明所有类型的单个位置
- 损坏标签很容易(无论是意外还是故意的)
一个更复杂的解决方案,可以作为良好的调试帮助,是引入一个
每种类型的注册表。缺点是需要对现有类型进行仪器化才能使其工作,但它足够简单,并且涉及更多的
运行时开销。但是嘿:
它有效!
template <typename> class Registrable;
class Registry {
public:
template <typename> friend class Registrable;
template <typename T>
static T* Cast(void*);
private:
struct TagType {};
using Key = std::pair<TagType const*, void*>;
using Store = std::set<Key>;
template <typename T>
static void Register(Registrable<T>* t);
template <typename T>
static void Unregister(Registrable<T>* t);
static Store& Get();
};
template <typename T>
T* Registry::Cast(void* const pointer) {
TagType const* const tag = &Registrable<T>::Tag;
if (Get().count(std::make_pair(tag, pointer)) == 0) { return nullptr; }
return static_cast<T*>(reinterpret_cast<Registrable<T>*>(pointer));
}
template <typename T>
void Registry::Register(Registrable<T>* t) {
TagType const* const tag = &T::Tag;
void* const pointer = reinterpret_cast<void*>(t);
Get().insert(std::make_pair(tag, pointer));
}
template <typename T>
void Registry::Unregister(Registrable<T>* t) {
TagType const* const tag = &T::Tag;
void* const pointer = reinterpret_cast<void*>(t);
Get().erase(std::make_pair(tag, pointer));
}
Registry::Store& Registry::Get() { static Store S; return S; }
template <typename T>
class Registrable {
public:
static Registry::TagType const Tag;
Registrable();
~Registrable();
Registrable(Registrable&&) = default;
Registrable& operator=(Registrable&&) = default;
Registrable(Registrable const&) = default;
Registrable& operator=(Registrable const&) = default;
};
template <typename T> Registry::TagType const Registrable<T>::Tag;
template <typename T>
Registrable<T>::Registrable() { Registry::Register(this); }
template <typename T>
Registrable<T>::~Registrable() { try { Registry::Register(this); } catch(...) {} }
*.c
源文件中定义它。这样可以避免使用void *
,因为您需要类型检查。 - amdixonvoid*
。 - Mike Seymour