使用模板函数安全地在多个类类型之间进行转换

时间:2016-03-31 04:49:35

标签: c++

我使用的第三方库需要将指针作为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,但由于上述原因而不可能。

1 个答案:

答案 0 :(得分:2)

您已经解释了为什么您的代码在评论中不起作用。你似乎想要一个替代方案,所以在这里。

http://ideone.com/24iaNv

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); }