2017-05-08 35 views
0

我正在重写一些现有代码 - 之前所有答案信息都存储在内存中的字符串数组中。根据数据类型,数据在各个地方进行了转换。以下是我正在瞄准的设置的快速模拟。基本上你有一些问题 - 存储在数据库中的答案的结构取决于数据类型。通常我会避免处理void *,并将它们转换为适当的类型 - 但我无法找到更好的解决方案,从而可以运行通用代码(通过lambda表达式),或者在数据类型已知的情况下具体运行。模板化的类在这种情况下不会起作用,因为所有的答案都需要存储在同一个向量中(因为有些算法会根据预定义的规则应用于所有答案)。将类型安全代码与运行时决策相结合

任何意见表示赞赏。

#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

您可以使用'varaint'而不是'void *'。 – Jarod42

+0

我在考虑变体 - 但是由于VC++并不完全支持C++ 17,所以我必须在我的项目中包含boost库(这不是主要问题)。变体成员是否仍然存在于基类中? – Floris

回答

1

variant是干净的做法。将其存储在父级中。

或者,在父级中提供variant<A,B> GetData()。现在访问被封装在返回的变体中。父母存储数据。

或者,提供一个virtual variant<A,B> GetData() = 0。子类型返回相关变体中的数据,或者是AB

或者,写virtual A* GetA() = 0; virtual B* GetB() = 0;。那么也许编写一个名为GetData<T>这样GetData<A>()电话GetA模板法等

或者,写virtual A* Get(tag_t<A>) = 0; virtual B* Get(tag_t<B>)=0;,其中

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

是用于分派的标签。现在您可以通过执行Get(tag<AddressData>)来调用正确的虚拟接口。

在这些虚拟情况下,数据存储在派生类型中。

+0

谢谢 - 这似乎更好。 @ Jarod42和Yakk,我很欣赏这个输入。我会尽量给出两个建议,并最终看看我更喜欢哪一个 – Floris