2017-05-08 7 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

'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