2016-03-31 13 views
0

私が使用しているサードパーティライブラリでは、ポインタをvoid*として渡す必要があります。いつでも、このポインタは継承チェーンのいくつかのクラスのうちの1つにキャストする必要があるかもしれません。これは多重継承が関与しているときに失敗するので、これを安全に行うための私の考案した方法は、クラス型をポインタと共に伝播して、目的の型に適切にキャストすることです。複数のクラスタイプ間で安全にキャストするテンプレート関数を使用する

#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ではなくstatic_castdynamic_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*をいくつかの異なるタイプにキャストする必要があるために、いくつかの型の安全を強化することです。私はこれがどのように行われるか心配していません。上記は単に私が考え出すことのできる最高のものです。

EDITdynamic_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

なぜあなたは物事が複雑になり過ぎています。単純に 'ParentOne * p1 = child'を使わないのはなぜですか? –

+0

あなたのプログラムは完全に私には似ています。どのようなコンパイラとフラグを使用していますか? –

+0

@MohitJainそのコードはデモンストレーション用です。 'child'が' void * 'に代入されると、親クラスにのみアクセスすることができます。 –

答えて

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

最も優れています。これは私が望んでいた正確な結果をもたらします。 –

+0

'static_assert'を許可するには、'!std :: is_same :: value'で 'false'を置き換えるように、テンプレートパラメータの条件に依存する必要があります。 – Jarod42

+0

@ Jarod42ありがとう、私はGCCを使用しないので、私は分かりませんでした。ここにいる人、ビジュアルスタジオ、GCCを知っていますか? – Jts

関連する問題