Given the abstract base class X, how to create another class of patterns D <T>, where T is the type of class originating from X?

I want to accept an object Message&that refers to either a class Message1or Message2. I want to be able to create MessageWithData<Message1>or MessageWithData<Message2>based on the base type of an object Message&. For example, see below:

class Message {};
class Message1 : public Message {};
class Message2 : public Message {};

template<typename Message1or2>
class MessageWithData : public Message1or2 { public: int x, y; }

class Handler()
{
public:
  void process(const Message& message, int x, int y)
  {
    // create object messageWithData whose type is 
    // either a MessageWithData<Message1> or a MessageWithData<Message2> 
    // based on message type.. how do I do this?
    //
    messageWithData.dispatch(...)
  }
};

messageWithData , Message, . , , , .

(, , http://jogear.net/dynamic-double-dispatch-and-templates)

+3
4

, .

, , , , .

, Data, , , .

-, . - , .

...


. , .

, . Shims, :

class Message {};
template <typename T> class MessageShim<T>: public Message {};
class Message1: public MessageShim<Message1> {};

:

  • Message, , , OO.
  • MessageShim<T> , .

, , , .

+1

, (runtime-) . , .

, . message message, Message1, Message2. , . , .

. , CRTP:

template<class TDerived>
class Message{};

class Message1 : public Message<Message1>{};
class Message2 : public Message<Message2>{};

template<class TMessage>
class MessageWithData : public TMessage { public: int x, y; };

class Handler{
public:
  template<class TMessage>
  void process(Message<TMessage> const& m, int x, int y){
    MessageWithData<TMessage> mwd;
    mwd.x = 42;
    mwd.y = 1337;
  }
};
+3

void process(const Message& message, int x, int y)
{
  // HERE
  messageWithData.dispatch(...)
}

MessageWithData<Message1>, MessageWithData<Message2>, , message Message1 Message1.

, MessageWithData<T> , T , , message.

+2

Xeo, , , - . , RTTI, , , , process() , . .

- , , , , RTTI , :

#include <iostream>
#include <stdexcept>

struct Base
{
    virtual ~Base() { }

    template <class Op>
    void for_rt_type(Op& op);
};

struct Derived1 : Base
{
    void f() { std::cout << "Derived1::f()\n"; }
};

struct Derived2 : Base
{
    void f() { std::cout << "Derived2::f()\n"; }
};

template <class Op>
void Base::for_rt_type(Op& op)
{
    if (Derived1* p = dynamic_cast<Derived1*>(this))
        op(p);
    else if (Derived2* p = dynamic_cast<Derived2*>(this))
        op(p);
    else
        throw std::runtime_error("unmatched dynamic type");
}

struct Op
{
    template <typename T>
    void operator()(T* p)
    {
        p->f();
    }
};

int main()
{
    Derived1 d1;
    Derived2 d2;
    Base* p1 = &d1;
    Base* p2 = &d2;
    Op op;
    p1->for_rt_type(op);
    p2->for_rt_type(op);
}

. factory : -}.

, for_rt_type : , "" , . , , API . , , , : (Op s), :

  • , ..
  • ,
    • eg. Derived1::value_typeis int, Derived2::value_typeis double- allows the algorithms for each to be efficient and use appropriate rounding, etc. Similarly for different types of containers, where only a common API is used.
  • You can use template metaprogramming, SFINAE, etc. to customize behavior along a specific path.

Personally, I believe that knowledge and the ability to apply this method (although rarely) is an important part of the assimilation of polymorphism.

+1
source

All Articles