Between static & dynamic polymorphism...

Discussion in 'C++' started by rwf_20, Nov 16, 2005.

  1. rwf_20

    rwf_20 Guest

    I just wanted to throw this up here in case anyone smarter than me has
    a suggestion/workaround:

    Problem:

    I have a classic producer/consumer system which accepts 'commands' from
    a socket and 'executes' them. Obviously, each different command (there
    are ~20 currently) has its own needed functionality. The dream goal
    here would be to remove all knowledge of the nature of the command at
    runtime. That is, I don't want ANY switch/cases or if/elses to
    determine what command has been received, etc:

    1. Accept command from socket.
    2. Create generic 'command' object with command data.
    3. Call 'execute' on command object.
    4. Command-specific code runs.

    So, there's two obvious ways that are _close_:

    Using static polylmorphism:

    template <unsigned long commandCode>
    class base_command {
    void execute();
    .....
    };

    // command 1
    void base_command<1>::execute() {
    ....command 1 specific code...
    }
    void base_command<2>::execute() {
    ....command 2 specific code...
    }

    int main() {
    unsigned long commandCode = //read command in somehow
    base_command<commandCode>().execute(); //obviously doesn't work
    because the compiler doesn't know 'commandCode' at compile-time
    }


    Using dynamic polymorphism:

    class base_command {
    virtual void execute() = 0;
    };

    class command_1 : public base_command {
    virtual void execute() {
    ...command 1 specific code...
    }
    };

    class command_2 : public base_command {
    .... etc...
    };

    int main() {
    unsigned long commandCode = // read command in somehow
    base_command* c = // some logic to determine subclass from (huge
    switch/case)
    }


    So, I'd love to use static polymorphism, but I'm fairly certain this
    can't and never will happen, due to no compile-time knowledge of the
    command code. I'm currently implementing the dynamic method, but I
    HATE the huge switch/case needed to determine command type. The
    executable size should be the same in all cases, but what I'm really
    looking for is a) cleanliness and b) efficiency. I'm on an embedded
    system, so using polymorphism makes me sick to my stomach. But, I
    guess it's either that or one class with an enormous if/else.

    I've got some other crazy half-ideas like a map of constructor function
    objects indexed by command code (boost probably has something nutty
    that might half-support this). But for the most part I think I'm
    stuck.

    Unless anyone has any thoughts...


    Thanks,
    Ryan
     
    rwf_20, Nov 16, 2005
    #1
    1. Advertising

  2. rwf_20 wrote:
    > I just wanted to throw this up here in case anyone smarter than me has
    > a suggestion/workaround:
    >
    > Problem:
    >
    > I have a classic producer/consumer system which accepts 'commands' from
    > a socket and 'executes' them. Obviously, each different command (there
    > are ~20 currently) has its own needed functionality. The dream goal
    > here would be to remove all knowledge of the nature of the command at
    > runtime. That is, I don't want ANY switch/cases or if/elses to
    > determine what command has been received, etc:


    Look up "object factory" on the web and get Modern C++ Design by
    Alexandrescu. Or simply define a std::map<id, function_pointer>.


    Jonathan
     
    Jonathan Mcdougall, Nov 16, 2005
    #2
    1. Advertising

  3. Alternatively use boost::function<void (param_t)>.

    Example:

    typedef boost::function<void (void)> ConsumerCommand;
    ConsumerCommand cmd;
    bool gotIt = consumerQueue.getNext(cmd);
    if(gotIt) cmd();
     
    Jeremy Jurksztowicz, Nov 16, 2005
    #3
  4. rwf_20

    werasm Guest

    rwf_20 wrote:
    > I just wanted to throw this up here in case anyone smarter than me has
    > a suggestion/workaround:
    >



    I don't know whether I'm smarter, thats (smarter in terms of what)...
    but

    You need to abstract dynamic and static polymorphism from each other.
    You want to be able to use dynamic polymorphism to execute via one
    interface, but static polymorphism to call something specific (I don't
    know whether this makes sense).

    You can never bind the sender of your command to a receiver type, as it
    may want to send msgs to arbitrary receivers. For this reason, your
    base should use dynamic polymorphism (I'll be brief).

    struct BaseCmd
    {
    void execute() = 0;
    BaseCmd* clone() const = 0; //I know of better clone implementations
    :)
    };

    class Client //Going to call Cmd.execute
    {
    void associate( const BaseCmd& cmd ){ cmd_ = cmd.clone(); }
    //...
    BaseCmd* cmd_;
    };

    template <class T>
    class MyCmd : public BaseCmd
    {
    //Implements execute.
    };

    Now we win by creating 1 command that represents all type T's, but
    Client is oblivious as the BaseCmd is not type dependent. Winning both
    ways by abstracting dynamic and static parts.

    For more information, you can also refer to Herb Sutters article
    "Elegant function call wrappers".

    Kind regards,

    Werner
     
    werasm, Nov 16, 2005
    #4
  5. rwf_20

    rwf_20 Guest

    Thanks for the suggestions, all.

    I've got something along these lines now. While not a complete
    solution, I do like the top-level syntax, which is the general goal.

    // base_command.h
    #include <boost/function.hpp>

    typedef boost::function<void (void)> exeFunction;

    std::map<unsigned long, exeFunction> constructorMapG;

    #define CMDCODE_ACQUIRE 0x1
    #define CMDCODE_ABORT 0x3
    #define CMDCODE_LASERTEST 0x18
    #define CMDCODE_FIRELASER 0x19

    template <unsigned long commandCode>
    class base_command {
    public:
    static void execute() { printf("in base_execute()\n"); }
    };

    void base_command<CMDCODE_FIRELASER>::execute() {
    printf("in fire_laser execute()\n");
    }
    void base_command<CMDCODE_ACQUIRE>::execute() {
    printf("in acquire() execute()\n");
    }
    void base_command<CMDCODE_ABORT>::execute() {
    printf("in abort() execute()\n");
    }
    void base_command<CMDCODE_LASERTEST>::execute() {
    printf("in laser_test() execute()\n");
    }

    void registerConstructors() {
    constructorMapG[CMDCODE_ABORT] = base_command<CMDCODE_ABORT>::execute;
    constructorMapG[CMDCODE_ACQUIRE] =
    base_command<CMDCODE_ACQUIRE>::execute;
    constructorMapG[CMDCODE_LASERTEST] =
    base_command<CMDCODE_LASERTEST>::execute;
    constructorMapG[CMDCODE_FIRELASER] =
    base_command<CMDCODE_FIRELASER>::execute;
    }

    typedef std::map<unsigned long, exeFunction>::const_iterator mapItr;

    exeFunction getMeAnExecute(const unsigned long code) {
    mapItr m = constructorMapG.find(code);
    if (m != constructorMapG.end()) return (*m).second;
    else // throw exception denoting invalid command
    }


    int main() {
    unsigned long cmdCode = // get command from socket
    getMeAnExecute(cmdCode)();
    }

    Any more thoughts on this or another solution are welcome.

    Thanks,
    Ryan
     
    rwf_20, Nov 16, 2005
    #5
  6. rwf_20

    Puppet_Sock Guest

    rwf_20 wrote:
    > I have a classic producer/consumer system which accepts 'commands' from
    > a socket and 'executes' them. Obviously, each different command (there
    > are ~20 currently) has its own needed functionality. The dream goal
    > here would be to remove all knowledge of the nature of the command at
    > runtime. That is, I don't want ANY switch/cases or if/elses to
    > determine what command has been received, etc:

    [snip]

    Ok, the discussion that followed this was great, and I will need to
    be learning from that.

    I'm just wondering, is 20 entries in a switch/case really that
    horrible?
    For that matter, would 100 entries be really that horrible? The time
    spent by your code looking up the correct entry in a switch/case is
    not going to be a large fraction of total run time. The cases can
    easily
    be made quite brief by making them function calls or some such.
    Switch/case methods are easy to design, easy to document, easy to
    debug, easy to maintain.

    Especially if you use some variation of the standard map so that
    you don't need to worry about converting the incoming msgs to
    integers.

    So, I'm wondering what it is about switch/case that bothers you so
    much.
    It makes me think I must be missing something important.
    Socks
     
    Puppet_Sock, Nov 16, 2005
    #6
  7. rwf_20

    rwf_20 Guest

    > So, I'm wondering what it is about switch/case that bothers you so much.
    > It makes me think I must be missing something important.


    Not really, no. My problem boils down to preference, really. What you
    say is true; a 100 switch/case block is not a big deal at all. In
    fact, this is what I've had previously in my scenario.

    What motivated my post is that, for N commands, the switch case is at
    least N extra lines of specialized code just to determine which one of
    N specialized functions to call! Sure, you can wrap it up cleanly,
    etc...but I've already written T::execute() N times -- I felt I
    deserved some way to avoid creating T in N different ways :). What
    sucks is that there is _almost_ a way, if you could only instantiate a
    template with a variable (I know, I know, this is impossible(?)).

    Ryan
     
    rwf_20, Nov 16, 2005
    #7
  8. rwf_20 wrote:
    > > So, I'm wondering what it is about switch/case that bothers you so much.
    > > It makes me think I must be missing something important.

    >
    > Not really, no. My problem boils down to preference, really. What you
    > say is true; a 100 switch/case block is not a big deal at all. In
    > fact, this is what I've had previously in my scenario.


    IMO, event a single test on an object identity is a big deal. It should
    be avoided when possible.

    > What motivated my post is that, for N commands, the switch case is at
    > least N extra lines of specialized code just to determine which one of
    > N specialized functions to call! Sure, you can wrap it up cleanly,
    > etc...but I've already written T::execute() N times -- I felt I
    > deserved some way to avoid creating T in N different ways :).


    The problem here, I guess, is not the hierarchy, but the object
    creation. You get an ID at runtime and need to create the appropriate
    object. One you get the object, it is only a matter of calling a
    virtual function.

    Creating the object is usually achieved using a map of id=>creator. The
    creator can be a function, another clonable object or whatever. Then,
    you must make sure every creator has a unique id and finally register
    them all in the map.

    > What
    > sucks is that there is _almost_ a way, if you could only instantiate a
    > template with a variable (I know, I know, this is impossible(?)).


    That does not make sense. It's like trying to have the address of a
    variable as a compile-time constant. A variable is a run-time entity. A
    template is a compile-time entity. At run-time, no "template" exist,
    only concrete functions. At compile-time, no "variable" exist, only
    names. Templates are a way in C++ to "automatically" create a family of
    functions at compile-time. It's syntactic sugar.


    Jonathan
     
    Jonathan Mcdougall, Nov 16, 2005
    #8
  9. rwf_20

    Kai-Uwe Bux Guest

    rwf_20 wrote:

    > I just wanted to throw this up here in case anyone smarter than me has
    > a suggestion/workaround:
    >
    > Problem:
    >
    > I have a classic producer/consumer system which accepts 'commands' from
    > a socket and 'executes' them. Obviously, each different command (there
    > are ~20 currently) has its own needed functionality. The dream goal
    > here would be to remove all knowledge of the nature of the command at
    > runtime. That is, I don't want ANY switch/cases or if/elses to
    > determine what command has been received, etc:
    >
    > 1. Accept command from socket.
    > 2. Create generic 'command' object with command data.
    > 3. Call 'execute' on command object.
    > 4. Command-specific code runs.
    >
    > So, there's two obvious ways that are _close_:
    >
    > Using static polylmorphism:
    >
    > template <unsigned long commandCode>
    > class base_command {
    > void execute();
    > ....
    > };
    >
    > // command 1
    > void base_command<1>::execute() {
    > ...command 1 specific code...
    > }
    > void base_command<2>::execute() {
    > ...command 2 specific code...
    > }
    >
    > int main() {
    > unsigned long commandCode = //read command in somehow
    > base_command<commandCode>().execute(); //obviously doesn't work
    > because the compiler doesn't know 'commandCode' at compile-time
    > }

    [snip]
    >
    > So, I'd love to use static polymorphism, but I'm fairly certain this
    > can't and never will happen, due to no compile-time knowledge of the
    > command code. I'm currently implementing the dynamic method, but I
    > HATE the huge switch/case needed to determine command type. The
    > executable size should be the same in all cases, but what I'm really
    > looking for is a) cleanliness and b) efficiency. I'm on an embedded
    > system, so using polymorphism makes me sick to my stomach. But, I
    > guess it's either that or one class with an enormous if/else.


    You can trick a template into generating the code for the if/else:

    #include <iostream>


    template <unsigned long N>
    void print ( void ) { std::cout << N << '\n'; }

    struct eval {

    typedef unsigned long enum_type;
    static enum_type const first = 0;
    static enum_type const last = 50;

    typedef int value_type;

    template < unsigned long N >
    static
    int function ( void ) {
    print<N>();
    return 0;
    }

    };

    template < typename eval >
    struct tpl_bin_search {

    typedef typename eval::enum_type Enum;

    template < Enum first, Enum last >
    static
    typename eval::value_type alg ( Enum val ) {
    if ( first == last ) {
    return( eval::template function<first>() );
    }
    if ( (Enum)( first + ( last-first )/2 ) < val ) {
    return( tpl_bin_search< eval >::template
    alg< (Enum)( last - ( last-first )/2), last >( val ) );
    } else {
    return( tpl_bin_search< eval >::template
    alg< first, (Enum)( first + ( last-first )/2) >( val ) );
    }
    }

    };

    template < typename eval >
    typename eval::value_type bin_search ( typename eval::enum_type val ) {
    return( tpl_bin_search< eval >::template
    alg< eval::first, eval::last >( val ) );
    }

    int main ( void ) {
    bin_search<eval>( 4 );
    }


    Best

    Kai-Uwe Bux
     
    Kai-Uwe Bux, Nov 16, 2005
    #9
  10. rwf_20

    rwf_20 Guest

    > Creating the object is usually achieved using a map of id=>creator. The
    > creator can be a function, another clonable object or whatever. Then,
    > you must make sure every creator has a unique id and finally register
    > them all in the map.


    Yes, this is what I have.

    > That does not make sense. It's like trying to have the address of a
    > variable as a compile-time constant. A variable is a run-time entity. A
    > template is a compile-time entity. At run-time, no "template" exist,
    > only concrete functions. At compile-time, no "variable" exist, only
    > names. Templates are a way in C++ to "automatically" create a family of
    > functions at compile-time. It's syntactic sugar.


    Yes, I understand why it can't work. As the subject implies, a
    completely clean solution would lie somewhere in between static &
    dynamic. I'm sure this could be achieved with a variety of compiler
    hacks, but I'm also sure the results would blur the (measurable)
    benefits of each pure method.

    Ryan
     
    rwf_20, Nov 16, 2005
    #10
  11. rwf_20

    Jim Langston Guest

    "rwf_20" <> wrote in message
    news:...
    >I just wanted to throw this up here in case anyone smarter than me has
    > a suggestion/workaround:
    >
    > Problem:
    >
    > I have a classic producer/consumer system which accepts 'commands' from
    > a socket and 'executes' them. Obviously, each different command (there
    > are ~20 currently) has its own needed functionality. The dream goal
    > here would be to remove all knowledge of the nature of the command at
    > runtime. That is, I don't want ANY switch/cases or if/elses to
    > determine what command has been received, etc:
    >
    > 1. Accept command from socket.
    > 2. Create generic 'command' object with command data.
    > 3. Call 'execute' on command object.
    > 4. Command-specific code runs.
    >
    > So, there's two obvious ways that are _close_:
    >
    > Using static polylmorphism:
    >
    > template <unsigned long commandCode>
    > class base_command {
    > void execute();
    > ....
    > };
    >
    > // command 1
    > void base_command<1>::execute() {
    > ...command 1 specific code...
    > }
    > void base_command<2>::execute() {
    > ...command 2 specific code...
    > }
    >
    > int main() {
    > unsigned long commandCode = //read command in somehow
    > base_command<commandCode>().execute(); //obviously doesn't work
    > because the compiler doesn't know 'commandCode' at compile-time
    > }
    >
    >
    > Using dynamic polymorphism:
    >
    > class base_command {
    > virtual void execute() = 0;
    > };
    >
    > class command_1 : public base_command {
    > virtual void execute() {
    > ...command 1 specific code...
    > }
    > };
    >
    > class command_2 : public base_command {
    > ... etc...
    > };
    >
    > int main() {
    > unsigned long commandCode = // read command in somehow
    > base_command* c = // some logic to determine subclass from (huge
    > switch/case)
    > }
    >
    >
    > So, I'd love to use static polymorphism, but I'm fairly certain this
    > can't and never will happen, due to no compile-time knowledge of the
    > command code. I'm currently implementing the dynamic method, but I
    > HATE the huge switch/case needed to determine command type. The
    > executable size should be the same in all cases, but what I'm really
    > looking for is a) cleanliness and b) efficiency. I'm on an embedded
    > system, so using polymorphism makes me sick to my stomach. But, I
    > guess it's either that or one class with an enormous if/else.
    >
    > I've got some other crazy half-ideas like a map of constructor function
    > objects indexed by command code (boost probably has something nutty
    > that might half-support this). But for the most part I think I'm
    > stuck.
    >
    > Unless anyone has any thoughts...


    Sounds like a map problem to me. Build a map with the key being your
    commands, and the object being a factory for the object to be created for
    that command. Receive the command, find it in the map, build the object,
    run execute. It would be run time polymorphism though, but no case
    statements involved, just building the map.

    Although when it comes down to it, one line to build each map object, one
    line for each cast statement. Somehow you have to associate the command
    with the object to be created, and this will take at least one line of code
    per command/object.
     
    Jim Langston, Nov 17, 2005
    #11
  12. rwf_20

    werasm Guest

    rwf_20 wrote:
    > Thanks for the suggestions, all.
    >
    > I've got something along these lines now. While not a complete
    > solution, I do like the top-level syntax, which is the general goal.
    >
    > // base_command.h
    > #include <boost/function.hpp>


    Why are you includin
    >
    > typedef boost::function<void (void)> exeFunction;
    >
    > std::map<unsigned long, exeFunction> constructorMapG;
    >
    > #define CMDCODE_ACQUIRE 0x1
    > #define CMDCODE_ABORT 0x3
    > #define CMDCODE_LASERTEST 0x18
    > #define CMDCODE_FIRELASER 0x19
    >
    > template <unsigned long commandCode>
    > class base_command {
    > public:
    > static void execute() { printf("in base_execute()\n"); }
    > };
    >
    > void base_command<CMDCODE_FIRELASER>::execute() {
    > printf("in fire_laser execute()\n");
    > }
    > void base_command<CMDCODE_ACQUIRE>::execute() {
    > printf("in acquire() execute()\n");
    > }
    > void base_command<CMDCODE_ABORT>::execute() {
    > printf("in abort() execute()\n");
    > }
    > void base_command<CMDCODE_LASERTEST>::execute() {
    > printf("in laser_test() execute()\n");
    > }
    >
    > void registerConstructors() {
    > constructorMapG[CMDCODE_ABORT] = base_command<CMDCODE_ABORT>::execute;
    > constructorMapG[CMDCODE_ACQUIRE] =
    > base_command<CMDCODE_ACQUIRE>::execute;
    > constructorMapG[CMDCODE_LASERTEST] =
    > base_command<CMDCODE_LASERTEST>::execute;
    > constructorMapG[CMDCODE_FIRELASER] =
    > base_command<CMDCODE_FIRELASER>::execute;
    > }
    >
    > typedef std::map<unsigned long, exeFunction>::const_iterator mapItr;
    >
    > exeFunction getMeAnExecute(const unsigned long code) {
    > mapItr m = constructorMapG.find(code);
    > if (m != constructorMapG.end()) return (*m).second;
    > else // throw exception denoting invalid command
    > }
    >
    >
    > int main() {
    > unsigned long cmdCode = // get command from socket
    > getMeAnExecute(cmdCode)();
    > }
    >
    > Any more thoughts on this or another solution are welcome.


    Hi Ryan,

    You have to ask yourself how this would have looked if you implemented
    it using switch case style. The problem with your problem is that the
    code is not known statically. For this reason you have to use either a
    switch, or a data-structure like a map to do what you want to do. You
    don't seem to gain anything wrt. scalability, and you've certainly
    compromised efficiency. I therefore don't see any benefits whatsoever
    when looking at your solution.

    The fact is, if you want to gain flexibility, you will loose some
    efficiency. The question is, how much efficiency are you willing to
    loose. Is scalability so important that you are willing to compromise
    efficiency. On the other hand, is efficiency so important that you
    don't want to make it scalable? By steering away from switch/case style
    programming in the case here above, you've seemingly made things more
    complicated, less efficient (using map instead of switch) and less
    scalable (to add one additional case, you are required to specialize a
    member function and add some code to register this for each case.)

    IMHO a bad solution :). Face it, your commandCode is not know at
    compile time. Therefore using static polymorphism to the extent to
    which you want to, is not viable.

    Kind regards

    Werner
     
    werasm, Nov 17, 2005
    #12
  13. rwf_20

    rwf_20 Guest

    > The fact is, if you want to gain flexibility, you will loose some
    > efficiency.


    Surely, you're right Werner. It seems the holy grail of cleanliness &
    efficiency are not completely obtainable here. The best way, as far as
    I can tell, would be a mix. Specialize the execute() template for each
    command. Then, call the appropriate specialization in the switch-case.
    This would eliminate the dynamic polymophism overhead with similar
    code size.

    I'm basically doing this now, but I'm just using the map because I like
    the idea of calling a generic function at the top level. So it seems
    the tradeoff is a map find vs. a switch/case.

    Am I missing something?

    Ryan
     
    rwf_20, Nov 17, 2005
    #13
  14. rwf_20

    mlimber Guest

    rwf_20 wrote:
    > > The fact is, if you want to gain flexibility, you will loose some
    > > efficiency.

    >
    > Surely, you're right Werner. It seems the holy grail of cleanliness &
    > efficiency are not completely obtainable here. The best way, as far as
    > I can tell, would be a mix. Specialize the execute() template for each
    > command. Then, call the appropriate specialization in the switch-case.
    > This would eliminate the dynamic polymophism overhead with similar
    > code size.
    >
    > I'm basically doing this now, but I'm just using the map because I like
    > the idea of calling a generic function at the top level. So it seems
    > the tradeoff is a map find vs. a switch/case.
    >
    > Am I missing something?
    >
    > Ryan


    Ryan, I recently solved this same problem. I used an object factory
    from chapter 8 of _Modern C++ Design_, as Jonathan also suggested
    above. The factory is intended to allow extensibility and cleanliness
    of dynamic polymorphism while avoiding the switch statements that you
    rightly dread. To use the factory (which can be downloaded free from
    http://sourceforge.net/projects/loki-lib), one simply associates an ID
    with each command, registers that ID and the relevant creation function
    with the factory, and supplies that ID to the factory when it's time to
    create the object. Compare:

    struct Cmd
    {
    virtual void Execute() = 0;
    virtual void Deserialize( const void*, unsigned ) = 0;
    };

    // IDs
    enum CmdID { CMD_1, CMD_2 /* etc. */ };

    struct Cmd1 : Cmd
    {
    static const CmdID ID = CMD_1;
    static Cmd* Create() { return new Cmd1; }

    void Execute() { /* Do something */ }
    void Deserialize( const void*, unsigned ) { /* init members */ }
    // ...
    };

    struct Cmd2 : Cmd
    {
    static const CmdID ID = CMD_2;
    static Cmd* Create() { return new Cmd2; }

    void Execute() { /* Do something else */ }
    void Deserialize( const void*, unsigned ) { /* init members */ }
    // ...
    };

    // Our factory
    Loki::Factory<Cmd, int> cmdFactory;

    // Register each concrete class with the factory
    const bool reg1 = cmdFactory.Register( Cmd1::ID, Cmd1::Create );
    const bool reg2 = cmdFactory.Register( Cmd2::ID, Cmd2::Create );

    // Get a new command
    Cmd* ConvertSocketDataToCmd(
    const void* const data,
    const unsigned size )
    {
    // Validate fn params
    assert( data && size );

    // First int-sized datum is always the ID
    const int id = *static_cast<const int*>( data );

    // Get the correct command
    Cmd* const cmd = cmdFactory.CreateObject( id );

    // Fill in the cmd
    cmd->Deserialize( data, size );

    return cmd;
    }


    I actually use much of the same code (i.e. same source files) on both
    sides of my communication barrier. The producer simply implements
    Cmd::Execute() to send the command across, while the consumer
    implements it to perform the desired action on the other side of the
    barrier. (The real thing also has serialization facilities, responses
    to the commands, etc., but you get the idea.)

    Compare also the FAQ on serialization
    (http://www.parashift.com/c -faq-lite/serialization.html) and the
    Boost serialization library
    (http://boost.org/libs/serialization/doc/index.html).

    Cheers! --M
     
    mlimber, Nov 17, 2005
    #14
  15. rwf_20

    rwf_20 Guest

    mlimber wrote:
    > Ryan, I recently solved this same problem. I used an object factory
    > from chapter 8 of _Modern C++ Design_, as Jonathan also suggested
    > above. The factory is intended to allow extensibility and cleanliness
    > of dynamic polymorphism while avoiding the switch statements that you
    > rightly dread. To use the factory (which can be downloaded free from
    > http://sourceforge.net/projects/loki-lib), one simply associates an ID
    > with each command, registers that ID and the relevant creation function
    > with the factory, and supplies that ID to the factory when it's time to
    > create the object.


    This is almost exactly what I'm doing now. But, instead of creating a
    derived class for each command, I'm simply specializing execute() in
    the base class. So, instead of registering each Cmd object with the
    factory, I'm registering each specialization of execute() in a map.
    Plus, it's not clear to me how it could be any slower (other than my
    writing it as opposed to Alexandrescu) -- it seems my method trades all
    the dynamic overhead for a map lookup.

    Thanks for the input,
    Ryan
     
    rwf_20, Nov 17, 2005
    #15
  16. rwf_20

    mlimber Guest

    rwf_20 wrote:
    > mlimber wrote:
    > > Ryan, I recently solved this same problem. I used an object factory
    > > from chapter 8 of _Modern C++ Design_, as Jonathan also suggested
    > > above. The factory is intended to allow extensibility and cleanliness
    > > of dynamic polymorphism while avoiding the switch statements that you
    > > rightly dread. To use the factory (which can be downloaded free from
    > > http://sourceforge.net/projects/loki-lib), one simply associates an ID
    > > with each command, registers that ID and the relevant creation function
    > > with the factory, and supplies that ID to the factory when it's time to
    > > create the object.

    >
    > This is almost exactly what I'm doing now. But, instead of creating a
    > derived class for each command, I'm simply specializing execute() in
    > the base class. So, instead of registering each Cmd object with the
    > factory, I'm registering each specialization of execute() in a map.
    > Plus, it's not clear to me how it could be any slower (other than my
    > writing it as opposed to Alexandrescu) -- it seems my method trades all
    > the dynamic overhead for a map lookup.
    >
    > Thanks for the input,
    > Ryan


    It won't likely be slower unless you call execute many times or much
    faster since you're simply trading a map lookup at the creation for one
    at the execution. The inefficiency of a virtual call is over-hyped in
    some circles (I work in an embedded environment, too) and, in practice,
    is often dwarfed and rendered insignificant by the body of the virtual
    function itself.

    My preference would be to go for the most idiomatic, understandable,
    and maintainable code, and IMHO, the funky stuff is better hidden at
    the creation than in the operation of the class. For that reason, I
    chose the factory approach.

    Cheers! --M
     
    mlimber, Nov 17, 2005
    #16
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. shablool
    Replies:
    5
    Views:
    484
    mlimber
    Oct 13, 2005
  2. Krivenok Dmitry
    Replies:
    13
    Views:
    1,438
    Axter
    Jun 1, 2006
  3. Replies:
    8
    Views:
    594
  4. PerlFAQ Server
    Replies:
    0
    Views:
    360
    PerlFAQ Server
    Jan 6, 2011
  5. PerlFAQ Server
    Replies:
    0
    Views:
    265
    PerlFAQ Server
    Apr 15, 2011
Loading...

Share This Page