Boost logo

Boost Users :

From: Steven Watanabe (watanabesj_at_[hidden])
Date: 2008-03-25 22:16:13


AMDG

Robert Dailey wrote:
>
>
> template<class F, class T>
> struct downcaster {
> typedef boost::remove_reference<T> arg_type;
> downcaster(F f) : f(f) {}
> template<class U>
> void operator()(const U& u) const {
> if(arg_type* arg = dynamic_cast<arg_type*>(&u)) {
> f(*arg);
> }
> }
> F f;
> };
> template<class T, class F>
> downcaster<F, T> make_downcaster(F f) {
> return(downcaster<F, T>(f));
> }
>
> In Christ,
> Steven Watanabe
>
>
> Sorry, I guess I didn't explain myself very well. Your class is cute
> but I don't think it is going to work, or perhaps I am
> misunderstanding its purpose. It would be great if you could show me
> how the boost::signal would be defined using your code above.

The type of the signal would be boost::signal<void (Packet const& p)>
When you register a function object with the signal, you would say:
make_downcaster<WalkPacket>(f) to get a function object that will
    a) call f(x) if x is a WalkPacket
    b) do nothing if x is something else.
I believe that this does in fact solve your problem but will be horribly
inefficient,
since it repeats the test for every function object.

> Basically, I have several types of packets. For example: WalkPacket,
> ChatPacket, QuestPacket, etc. When I'm receiving data from the
> network, I first accumulate a buffer of bytes and deserialize that
> into one of these packet structures. The data I'm receiving from the
> network will have an ID in there which maps to one of these types. For
> example, ID 1 would be WalkPacket, ID 2 is ChatPacket, and so forth.
> This is what my packet factory does, it takes an ID and returns a
> Packet* (which represents the actual concrete type that maps to the ID).
>
> When these packets are received and created, they are immediately sent
> to subscribers interested in that packet and then the packet is
> destroyed. Before I said I wanted to only use 1 boost.signal object,
> however I was incorrect. I need 1 boost.signal for each packet type.
> So I guess then what I need is a type of map that can take an integral
> as the key value and is capable of containing heterogeneously typed
> values. Boost.Fusion won't work here, since it does the exact
> opposite: it maps types to values (of course you could probably do
> some template tricks to get this to work). And the MPL library won't
> work either since it is strictly compile time, and we never know what
> ID's we're going to use until runtime.
>
> I need to be able to construct a map like this:
>
> enum PacketIDs
> {
> PID_WALKPACKET = 0,
> PID_CHATPACKET,
> PID_QUESTPACKET
> };
>
> // This is not a map of types!
> map<
> pair<PID_WALKPACKET, boost::signal<void (WalkPacket const&)> >,
> pair<PID_CHATPACKET, boost::signal<void (ChatPacket const&)> >,
> pair<PID_QUESTPACKET, boost::signal<void (QuestPacket const&)> >
> > m_signalMap;
>
>
> // And then to send the packet, at runtime we simply do:
> WalkPacket myWalkPacket;
> // Note that the value used to index into the map won't be so static
> in the real use case.
> m_signalMap[PID_WALKPACKET]( myWalkPacket );
>
>
> So I guess the real question is: Does boost have such a map?

Not to my knowledge. There are several approaches.
You can of course hard code the switch. If there are
not a lot of different packet types this may the easiest alternative.

#include <boost/signal.hpp>

enum PacketIDs
{
    PID_WALKPACKET = 0,
    PID_CHATPACKET,
    PID_QUESTPACKET
};

struct Packet {};
struct WalkPacket : Packet {};
struct ChatPacket : Packet {};
struct QuestPacket : Packet {};

struct SignalHolder {
    boost::signal<void (WalkPacket const&)> m_sigWalk;
    boost::signal<void (ChatPacket const&)> m_sigChat;
    boost::signal<void (QuestPacket const&)> m_sigQuest;
    void dispatch(PacketIDs id, Packet const& packet) {
        switch(id) {
            case PID_WALKPACKET: m_sigWalk(static_cast<WalkPacket
const&>(packet)); break;
            case PID_CHATPACKET: m_sigChat(static_cast<ChatPacket
const&>(packet)); break;
            case PID_QUESTPACKET: m_sigQuest(static_cast<QuestPacket
const&>(packet)); break;
        }
    }
};

It's possible to use the preprocessor to "automate" this but
that's extremely ugly.
You might also look into replacing Packet with boost::variant.

> Note that Boost.Fusion map would probably work great for this, however
> I'm stuck using boost 1.34.1, which doesn't have boost::fusion::map.

No it won't as you said above.

> If you're confused about my first post, that's because I was too. My
> first design choice was to use polymorphism, however that obviously
> won't work since we only want to dispatch packets to slots that are
> interested in that specific packet. Having 1 global signal for all
> packet subscribers is too wasteful. Doing it this way also enables us
> to be a little more type safe on the subscriber's end at least.

In Christ,
Steven Watanabe


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net