2017-05-08 13 views
0

私はいくつかの既存のコードを書き直しています。以前はすべての回答情報がメモリ内の文字列配列に格納されていました。データ型に基づいて、データは様々な場所で変換されました。以下は私が目指している設定の簡単な模擬です。基本的にいくつかの質問があります。データベースに格納されている回答の構造は、データ型によって異なります。一般的に私はvoid *を扱うことは避け、適切な型にキャストしますが、ラムダを使ってジェネリックコードを実行できるような優れたソリューションを見つけることはできませんでした。すべての解答を同じベクトルに格納する必要があるため、テンプレート化されたクラスは役に立ちません(事前定義されたルールに基づいてすべての解答に算術演算が適用されるため)。タイプセーフなコードと実行時の決定を組み合わせる

アドバイスありがとうございます。

#include <vector> 
#include <memory> 


struct AddressData 
{ 
    wchar_t Line1[50]; 
    wchar_t Line2[50]; 
    long CountrySeqNo; 

    AddressData() 
    { 
     memset(this, 0, sizeof(*this)); 
    }; 
}; 

struct GenericData 
{ 
    wchar_t value[200]; 

    GenericData() 
    { 
     memset(this, 0, sizeof(*this)); 
    }; 
}; 

enum class DataType 
    : short 
{ 
    GENERIC, 
    ADDRESS 
}; 

class AnswerBase 
{ 
protected: 
    const void* const data; 
    const DataType dataType; 

protected: 
    AnswerBase(const DataType datatype, const void* const _data) 
     : dataType(datatype), data(data) 
    { 
     if (data == nullptr) 
      throw std::exception("Data may not be initialized as NULL"); 
    }; 

public: 
    /* 
     Some generic methods here that would apply logic by means of lambdas etc - these would be overwritten in the derived classes 
    */ 

    template<typename T> const T& GetData() { static_assert(false, "The given type is not supported"); }; 

    template<> 
    const GenericData& GetData() 
    { 
     if (DataType::GENERIC != dataType) 
      throw std::exception("The requested type does not match the value that initialised data"); 

     return *static_cast<const GenericData* const>(data); 
    }; 
    template<> 
    const AddressData& GetData() 
    { 
     if (DataType::ADDRESS != dataType) 
      throw std::exception("The requested type does not match the value that initialised data"); 

     return *static_cast<const AddressData* const>(data); 
    }; 


}; 


class AddressAnswer 
    : public AnswerBase 
{ 
public: 
    AddressAnswer() 
     : AnswerBase(DataType::ADDRESS, &answer) 
    { 
    }; 

protected: 
    AddressData answer; 
}; 


class GenericAnswer 
    : public AnswerBase 
{ 
public: 
    GenericAnswer() 
     : AnswerBase(DataType::GENERIC, &answer) 
    { 
    }; 

protected: 
    GenericData answer; 
}; 


int main() 
{ 
    std::vector<std::shared_ptr<AnswerBase>> answers; 
    answers.push_back(std::make_shared<GenericAnswer>()); 
    answers.push_back(std::make_shared<AddressAnswer>()); 


    // In some parts of code - interact with generic methods without needing to check the underlying data type 
    // .... 
    // .... 

    // In parts of code where we know we are dealing with a given type - like saving to a DB 


    auto val1 = answers[0]->GetData<GenericData>().value; 
    auto val2 = answers[1]->GetData<AddressData>().Line1; 

    // this will give a runtime failure 
    //auto val3 = answers[0]->GetData<AddressData>().Line1; 


    return 0; 
} 
+0

'void *'の代わりに 'varaint'を使うかもしれません。 – Jarod42

+0

私は変種について考えていましたが、VC++はまだC++ 17をサポートしていないので、私はプロジェクトに大きなライブラリではない(大きな問題ではない)。バリアントメンバーは引き続き基本クラスに存在しますか? – Floris

答えて

1

variantこれを行うにはクリーンな方法です。親に保管してください。

また、親にvariant<A,B> GetData()を指定してください。現在、訪問は返されたバリアントにカプセル化されています。親はデータを格納します。

また、virtual variant<A,B> GetData() = 0を入力してください。子タイプは、そのバリアントにAまたはBのいずれかのデータを返します。

また、virtual A* GetA() = 0; virtual B* GetB() = 0;と記述してください。多分、等GetData<T>ようGetData<A>()そのコールGetA呼ばれるテンプレート法、あるいは

を書き込む

template<class T> 
struct tag_t { 
    using type=T; 
    constexpr tag_t(){} 
}; 
template<class T> 
constexpr tag_t<T> tag{}; 

をディスパッチするために使用されるタグであるvirtual A* Get(tag_t<A>) = 0; virtual B* Get(tag_t<B>)=0;を書きます。今すぐGet(tag<AddressData>)を実行して、適切な仮想インターフェイスを呼び出すことができます。

これらの仮想ケースでは、データは派生型に格納されます。

+0

ありがとうございます - それは良いようです。 @ Jarod42とYakk、私は入力を感謝します。私は両方の提案を試して、私が最後に好むものを見ます – Floris

関連する問題