2017-01-15 6 views
3

메시지 버스와 함께 사용중인 Message 구조가 있으며 메시지가있는 데이터를 보내고 싶습니다. 문제는 데이터가 유형이 다양하다는 것입니다. 어쩌면 하나의 메시지에 대해 나는 단지 하나의 int를 보내고 싶을 것이다. 그러나 다른 것에 대해서는 여러 개의 int, 문자열, 예를 들어 객체에 대한 포인터를 보내기를 원할 것이다. 나는 같은 것을 할 수있는 :STL 여러 유형의 값을 저장하는 컨테이너입니까?

struct Message { 
    std::map<int, int> intPayload; 
    std::map<int, std::string> strPayload; 
    short id; 
}; 

을뿐만 아니라 추하고 더러운이, 그리고 아마도 공간을 낭비하지만 포인터와 같은 비교적 이국적인 데이터 형식을 전달하려면 그 고려하지 않습니다 예를 들어 클래스의 인스턴스에. 이것을 위해 무엇을 사용해야합니까?

+1

기본 클래스와 가상 함수 상속 및 포인터를 사용하여 :

은 또한 정적 방문자를 사용할 수 있습니까? –

+1

가능한 한 내 첫 번째 추측은 다형성이 될 것입니다. – DeiDei

+0

@DeiDei 나는 약간 이해가되지만, 다형성은 클래스/구조체 상속과 관련이있다. 다형성이 어떻게 도움이 될까? – Accumulator

답변

1

이렇게하는 방법에는 여러 가지가 있습니다. 여기서 C++ 17의 std::variant으로 예이다 :

std::vector<std::variant<int, std::string>> vec1; 

vec1.emplace_back(1); 
vec1.emplace_back("hello"s); 

doSomethingWithInt(std::get<int>(vec1[0])); 
doSomethingWithString(std::get<std::string>(vec1[1])); 

vec1int 또는 std::string 하나를 것을 요소의 목록이다.

std::vector<std::variant<int, std::string>> vec2; 

// ... 

for(auto&& variant : vec1) { 
    variant.visit([](auto value){ 
     using t = decltype(value); 

     if constexpr (std::is_same_v<t, int>) { 
      std::cout << "value is a int!" << std::endl; 
     } else if constexpr (std::is_same_v<t, std::string>) { 
      std::cout << "value is a string!" << std::endl; 
     } 
    }); 
} 
+0

나는 자신의 경우에 메시지를 처리하는 클래스 내 변형에서 벗어날 유형이 무엇인지 말할 수 없다고 생각합니다. – skypjack

+0

Visual Studio 2015에서 작동하지 않습니다 (''을 포함 할 수 없음). 제가 잘못 했나요? C++ 17을 지원하지 않습니까? – Accumulator

+0

어떤 유형인지 알 수있는 많은 방법이 있습니다. 가장 좋은 방법은 메소드 visit, get_if 및 get을 사용하는 것입니다. 문서가 아주 완벽합니다. ['std :: variant'] (http : // en.cppreference.com/w/cpp/utility/variant) 페이지 on cppreference –

2

상속과 다형성을 사용하는 간단한 예 :

struct MessageBase 
{ 
    // The function to send *this* message to the receiver 
    virtual void send(ReceiverClass*) = 0; 
}; 

struct MessageInt : MessageBase 
{ 
    int payload; 

    void send(ReceiverClass* receiver) 
    { 
     // Code to send this message type to the receiver... 
    } 
}; 

struct MessageString : MessageBase 
{ 
    std::string payload; 

    void send(ReceiverClass* receiver) 
    { 
     // Code to send this message type to the receiver... 
    } 
}; 

// ... 

// Vector to store the messages 
std::vector<MessageBase*> messages; 

// Add a couple of messages 
messages.push_back(new MessageInt{123}); 
messages.push_back(new MessageString{"Foobar"}); 

// Send the message to some receiver 
for (auto const* message : messages) 
    message->send(some_reciver_object); 

모든 good book 당신에게 더 많은 정보를 제공 할 수 있어야합니다.

2

방문자 패턴을 기반으로 솔루션을 구성 할 수 있습니다. 최소한, 사용 예로서
는 :

Bus의 구현이 단순하다는 사실 따로 설정
struct Listener; 

struct Message { 
    virtual void accept(Listener &) = 0; 
}; 

struct SimpleMessage: Message { 
    void accept(Listener &) override; 
    int i; 
}; 

struct ComplexMessage: Message { 
    void accept(Listener &) override; 
    int i; 
    char c; 
    double d; 
}; 

struct Listener { 
    void visit(SimpleMessage &) {} 
    void visit(ComplexMessage &) {} 
    void listen(Message &m) { m.accept(*this); } 
}; 

void SimpleMessage::accept(Listener &l) { l.visit(*this); } 
void ComplexMessage::accept(Listener &l) { l.visit(*this); } 

struct Bus { 
    Bus(Listener *l): l{l} {} 
    void publish(Message &m) { l->listen(m); } 
private: 
    Listener *l; 
}; 

int main() { 
    Listener l; 
    Bus b{&l}; 

    SimpleMessage sm; 
    ComplexMessage cm; 

    b.publish(sm); 
    b.publish(cm); 
} 

, Listenervisit 멤버 함수는 가상 될 수 있습니다.
이렇게하면 모든 수신기를 해당 클래스에서 파생시키고 원하는 메서드를 재정의 할 수 있습니다.
Bus은 실제로 파생 된 유형이 무엇이든 Listener 세트를 허용하며 일반 Message을 허용합니다. 다른면에서 메시지는 자신을 오른쪽 파생 형식으로 승격시키고 지정된 리스너에 대한 참조를 전달합니다.

방문자 패턴 뒤에있는 기술은 double dispatching이라고도하며 더 자세히 살펴보고 싶습니다.