我使用的第三方库需要将指针作为void*
传递。在任何给定时间,此指针可能需要转换为继承链中的几个类之一。当涉及多重继承时,这会失败,因此我设计的安全实现方法是将类类型与指针一起传播,以便正确地转换为所需的类型。以下内容说明了预期目标:
#include <assert.h>
#include <stdint.h>
#include <cstddef>
#include <stdio.h>
enum class kTypeFlag { PARENTONE, PARENTTWO, CHILD };
#define CAST_TO_CLASS_TYPE(TO, FROM, POINTER) \
dynamic_cast<TO*>(static_cast<FROM*>(POINTER))
class ParentOne {
public:
ParentOne() { }
virtual ~ParentOne() { }
virtual size_t byte_size() const = 0;
};
class ParentTwo {
public:
ParentTwo(uint32_t id) : id_(id) { }
virtual ~ParentTwo() { }
uint32_t id() const { return id_; }
private:
const uint32_t id_;
};
class Child : public ParentOne, public ParentTwo {
public:
Child(uint32_t id) : ParentOne(), ParentTwo(id) { }
virtual ~Child() { }
size_t byte_size() const override { return sizeof(*this); }
};
template <class T>
T* Convert(void* pointer, kTypeFlag flag) {
switch (flag) {
case kTypeFlag::PARENTONE:
return CAST_TO_CLASS_TYPE(T, ParentOne, pointer);
case kTypeFlag::PARENTTWO:
return CAST_TO_CLASS_TYPE(T, ParentTwo, pointer);
case kTypeFlag::CHILD:
return CAST_TO_CLASS_TYPE(T, Child, pointer);
default:
assert(0 && "invalid flag to Convert");
}
}
int main() {
Child* child = new Child(5);
printf("byte_size: %zu\n", child->byte_size());
printf("id: %u\n", child->id());
ParentOne* p1 = Convert<ParentOne>(child, kTypeFlag::CHILD);
ParentTwo* p2 = Convert<ParentTwo>(child, kTypeFlag::CHILD);
printf("byte_size: %zu\n", p1->byte_size());
printf("id: %u\n", p2->id());
}
请注意,CAST_TO_CLASS_TYPE
使用的是dynamic_cast
而不是static_cast
。这是因为如果使用static_cast
,则构建将失败并显示:
run.cc:40:14: error: static_cast from 'ParentTwo *' to 'ParentOne *', which are not related by inheritance, is not allowed
return CAST_TO_CLASS_TYPE(T, ParentTwo, pointer);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
run.cc:9:3: note: expanded from macro 'CAST_TO_CLASS_TYPE'
static_cast<TO*>(static_cast<FROM*>(POINTER))
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
run.cc:53:19: note: in instantiation of function template specialization 'Convert<ParentOne>' requested here
ParentOne* p1 = Convert<ParentOne>(child, kTypeFlag::CHILD);
^
所以我的问题是:使用static_cast
时为什么构建失败?或者有完全不同的方法吗?最终目标是围绕需要将void*
转换为几种不同类型来强制执行某种类型的安全性。我并不关心如何做到这一点。以上就是我能想到的最好的。
编辑:请允许我澄清为什么使用dynamic_cast
并不理想。以下代码段编译正常:
ParentTwo* p2 = new ParentTwo(42);
ParentOne* p1 = Convert<ParentOne>(p2, kTypeFlag::PARENTTWO);
printf("id: %u\n", p2->id());
printf("byte_size: %zu\n", p1->byte_size());
但它在运行时中止。优选地,这将在编译时捕获。这就是为什么我想使用static_cast
,但由于上述原因而不可能。
答案 0 :(得分:2)
您已经解释了为什么您的代码在评论中不起作用。你似乎想要一个替代方案,所以在这里。
namespace details
{
template<typename To, kTypeFlag>
struct cex {To* operator()() { static_assert(!std::is_same<To, To>::value, "invalid flag to Convert");} };
template<typename To>
struct cex<To, kTypeFlag::PARENTONE> { To* operator()(void*p) { return static_cast<To*>(static_cast<ParentOne*>(p)); } };
template<typename To>
struct cex<To, kTypeFlag::PARENTTWO> { To* operator()(void*p) { return static_cast<To*>(static_cast<ParentTwo*>(p)); } };
template<typename To>
struct cex<To, kTypeFlag::CHILD> { To* operator()(void*p) { return static_cast<To*>(static_cast<Child*>(p)); } };
};
template <typename T, kTypeFlag f>
T* ConvertEx (void* pointer) { return details::cex<T, f>()(pointer); }