Alternative To Members As Callbacks?

Discussion in 'C++' started by none, Feb 10, 2010.

  1. none

    none Guest

    I have a "loader" class that loads 3D objects from disk. Initializing
    "loader" objects is very expensive in both time and space, because there's
    a lot to know about how to read a particular type of file.

    Hence, I cannot create a new instance of "loader" every time I want to load
    something. So I have several global loader objects that are initialized
    when the program starts up -- the "character_loader," the
    "monster_loader," the "weapon_loader," and so on. These are *not* classes
    derived from "loader," they are simply global instances of loader that are
    initialized differently so that they can load different types of objects.

    These global loaders need to be used by many other objects of many
    different types. This just means that the loader class itself needs to be
    generic. In other words, everyone knows about loader but loader doesn't
    know about anyone else.

    Now, whatever object it is that uses one of these global loaders, that
    object needs a way to be informed that something has happened, e.g., a
    texture was loaded, a light source was loaded, loading is finished, etc.

    What I *thought* that I wanted to do is have the loader use callback
    functions. Each object that wants to load something could assign a
    callback function to be invoked when important things happen within the
    loader. The problem (as warned in the FAQ) is that doing so would require
    some way for the loader to know the "this" pointer for each object. And to
    make matters worse, the type of object that "this" refers to will vary.

    Is this just a broken approach from the start?

    Thanks.
    none, Feb 10, 2010
    #1
    1. Advertising

  2. none

    tonydee Guest

    On Feb 10, 5:37 pm, none <> wrote:
    > I have a "loader" class that loads 3D objects from disk.  Initializing
    > "loader" objects is very expensive in both time and space, because there's
    > a lot to know about how to read a particular type of file.
    >
    > Hence, I cannot create a new instance of "loader" every time I want to load
    > something.  So I have several global loader objects that are initialized
    > when the program starts up -- the "character_loader," the
    > "monster_loader," the "weapon_loader," and so on.  These are *not* classes
    > derived from "loader," they are simply global instances of loader that are
    > initialized differently so that they can load different types of objects.
    >
    > These global loaders need to be used by many other objects of many
    > different types.  This just means that the loader class itself needs to be
    > generic.  In other words, everyone knows about loader but loader doesn't
    > know about anyone else.
    >
    > Now, whatever object it is that uses one of these global loaders, that
    > object needs a way to be informed that something has happened, e.g., a
    > texture was loaded, a light source was loaded, loading is finished, etc.
    >
    > What I *thought* that I wanted to do is have the loader use callback
    > functions.  Each object that wants to load something could assign a
    > callback function to be invoked when important things happen within the
    > loader.  The problem (as warned in the FAQ) is that doing so would require
    > some way for the loader to know the "this" pointer for each object.  And to
    > make matters worse, the type of object that "this" refers to will vary.
    >
    > Is this just a broken approach from the start?


    No, it's not a broken approach, just needs a tweak. What you want is
    known as the Observer pattern, full details are easily searched
    online. Summarily, use a base class containing virtual functions
    defining the possible callbacks, derive the observers from that base
    class, and provide a "register(Observer*)" function in your Loader
    class (probably appending the observer's address to a vector...).

    Cheers,
    Tony
    tonydee, Feb 10, 2010
    #2
    1. Advertising

  3. none

    qdii Guest

    >>  The problem (as warned in the FAQ) is that doing so would
    >> require some way for the loader to know the "this" pointer for each
    >> object.  And to make matters worse, the type of object that "this"
    >> refers to will vary.
    >>
    >> Is this just a broken approach from the start?




    > No, it's not a broken approach, just needs a tweak. What you want is
    > known as the Observer pattern, full details are easily searched online.
    > Summarily, use a base class containing virtual functions defining the
    > possible callbacks, derive the observers from that base class, and
    > provide a "register(Observer*)" function in your Loader class (probably
    > appending the observer's address to a vector...).



    Actually both ideas match : the Observer design pattern suggested by tony
    is really close to your first idea. The Loader indeed knows about a list
    of pointers to "this".

    Now you were concerned about the type of object pointed by "this". And
    the answer proposed by tony's design pattern is : "they are all
    Observers". Said differently, they all need to derive from the Observer
    class. This latter could be as simple as :

    class Observer
    {
    public:
    virtual void notify() = 0;
    };

    Now your loader will call "notify" on every observers.

    --
    qdii -- www.mathseconde.fr
    qdii, Feb 10, 2010
    #3
  4. none

    Richard Guest

    [Please do not mail me a copy of your followup]

    none <> spake the secret code
    <Xns9D1B109A1ADEDnonenonenone@69.16.186.8> thusly:

    >What I *thought* that I wanted to do is have the loader use callback
    >functions. Each object that wants to load something could assign a
    >callback function to be invoked when important things happen within the
    >loader. [...]


    Callback functions are what you do in C.

    In C++, use an interface instead.
    --
    "The Direct3D Graphics Pipeline" -- DirectX 9 draft available for download
    <http://legalizeadulthood.wordpress.com/the-direct3d-graphics-pipeline/>

    Legalize Adulthood! <http://legalizeadulthood.wordpress.com>
    Richard, Feb 10, 2010
    #4
  5. none

    Öö Tiib Guest

    On Feb 10, 8:18 pm, (Richard) wrote:
    > [Please do not mail me a copy of your followup]
    >
    > none <> spake the secret code
    > <Xns9D1B109A1ADEDnonenonen...@69.16.186.8> thusly:
    >
    > >What I *thought* that I wanted to do is have the loader use callback
    > >functions.  Each object that wants to load something could assign a
    > >callback function to be invoked when important things happen within the
    > >loader. [...]

    >
    > Callback functions are what you do in C.
    >
    > In C++, use an interface instead.


    Interface in wider sense is too generic and in narrow sense is java
    keyword. C++ has wide set of tools and lacks "one size fits all"
    ideologies. C++ developer can use everything that is provided on
    platform and language is it low or high. Use callbacks, interfaces,
    functor objects, events, messages, signals etc ... choice depends on
    the requirements to task under hand, qualification of workforce and so
    on.

    Slots and signals from boost library are probably cheapest way to get
    already implemented generic observer pattern.
    Öö Tiib, Feb 10, 2010
    #5
  6. none

    James Kanze Guest

    On Feb 10, 8:37 am, none <> wrote:

    [...]
    > These global loaders need to be used by many other objects of
    > many different types. This just means that the loader class
    > itself needs to be generic. In other words, everyone knows
    > about loader but loader doesn't know about anyone else.


    > Now, whatever object it is that uses one of these global
    > loaders, that object needs a way to be informed that something
    > has happened, e.g., a texture was loaded, a light source was
    > loaded, loading is finished, etc.


    > What I *thought* that I wanted to do is have the loader use
    > callback functions. Each object that wants to load something
    > could assign a callback function to be invoked when important
    > things happen within the loader. The problem (as warned in
    > the FAQ) is that doing so would require some way for the
    > loader to know the "this" pointer for each object. And to
    > make matters worse, the type of object that "this" refers to
    > will vary.


    > Is this just a broken approach from the start?


    More or less. See the observer pattern. Basically, the loader
    class defines an abstract observer type, through which it
    notifies what it has to notify. Objects that are interested in
    such notifications derive from this abstract class, and register
    with the loader.

    --
    James Kanze
    James Kanze, Feb 10, 2010
    #6
  7. none

    Richard Guest

    [Please do not mail me a copy of your followup]

    Callbacks are what you do in C because you don't have objects.

    To implement a conversation between two objects in C++, you use
    interfaces, i.e. pure abstract base class. The caller implements the
    interface expected by the called component so that the called
    component can talk back to the object that called it without being
    directly coupled to the concrete class of the calling object.

    Every time I see callback functions in C++ code, its because the
    programmer is thinking like a C programmer and callbacks are all you
    have in C. Interfaces are more typesafe and are the superior approach
    because you don't have to create all those little delegating functions
    that take a callback function and turn it back into a method call:

    void ResponseCallback(void *context)
    {
    static_cast<Collaborator *>(context)->Response();
    }

    If you ever find yourself writing the above code, and its not being
    imposed by a 3rd party library you don't control, then you really want
    an interface and not a callback.

    Whether this is or isn't an instantiation of the Observer pattern is a
    separate discussion.
    --
    "The Direct3D Graphics Pipeline" -- DirectX 9 draft available for download
    <http://legalizeadulthood.wordpress.com/the-direct3d-graphics-pipeline/>

    Legalize Adulthood! <http://legalizeadulthood.wordpress.com>
    Richard, Feb 11, 2010
    #7
  8. Am 11.02.2010 01:01, schrieb Richard:
    > [Please do not mail me a copy of your followup]
    >
    > Callbacks are what you do in C because you don't have objects.
    >
    > To implement a conversation between two objects in C++, you use
    > interfaces, i.e. pure abstract base class. The caller implements the
    > interface expected by the called component so that the called
    > component can talk back to the object that called it without being
    > directly coupled to the concrete class of the calling object.
    >
    > Every time I see callback functions in C++ code, its because the
    > programmer is thinking like a C programmer and callbacks are all you
    > have in C. Interfaces are more typesafe and are the superior approach
    > because you don't have to create all those little delegating functions
    > that take a callback function and turn it back into a method call:

    [...]

    In C++, you have std::tr1::function [1], which is the superior approach.
    You don't need an observer interface, you don't need delegating
    functions, just a function.

    [1] The one from Boost works the same.

    --
    Thomas
    Thomas J. Gritzan, Feb 11, 2010
    #8
  9. On Feb 10, 3:37 am, none <> wrote:
    > What I *thought* that I wanted to do is have the loader use callback
    > functions.  Each object that wants to load something could assign a
    > callback function to be invoked when important things happen within the
    > loader.  The problem (as warned in the FAQ) is that doing so would require
    > some way for the loader to know the "this" pointer for each object.  And to
    > make matters worse, the type of object that "this" refers to will vary.
    >
    > Is this just a broken approach from the start?


    No it isn't broken. You just need Don Clugston's delegates:

    http://www.codeproject.com/KB/cpp/FastDelegate.aspx

    With those you can efficiently bind and call member functions
    static or otherwise as well as free functions. Use them and be
    happy. Forget about the Observer pattern, use delegates instead.

    KHD
    Keith H Duggar, Feb 11, 2010
    #9
  10. none

    Richard Guest

    [Please do not mail me a copy of your followup]

    "Thomas J. Gritzan" <> spake the secret code
    <hkviq8$ion$> thusly:

    >Am 11.02.2010 01:01, schrieb Richard:
    >> [Please do not mail me a copy of your followup]
    >>
    >> Callbacks are what you do in C because you don't have objects.
    >>
    >> To implement a conversation between two objects in C++, you use
    >> interfaces, i.e. pure abstract base class. The caller implements the
    >> interface expected by the called component so that the called
    >> component can talk back to the object that called it without being
    >> directly coupled to the concrete class of the calling object.
    >>
    >> Every time I see callback functions in C++ code, its because the
    >> programmer is thinking like a C programmer and callbacks are all you
    >> have in C. Interfaces are more typesafe and are the superior approach
    >> because you don't have to create all those little delegating functions
    >> that take a callback function and turn it back into a method call:

    >[...]
    >
    >In C++, you have std::tr1::function [1], which is the superior approach.
    >You don't need an observer interface, you don't need delegating
    >functions, just a function.


    Yes, tr1::function is a generalized callback mechanism that supports
    static functions, function objects (anything providing operator()) and
    pointer to member functions. Essentially the template magic is just
    writing the delegating function for you.

    tr1::function can be used to implement Observer. As a design pattern,
    Observer is not coupled to a specific language construct or
    implementation strategy. It is a pattern, not a library.

    However, given that interfaces have been around in C++ for a decade (or
    more, I'm not a C++ historian) and people are *still* writing C-style
    callbacks with void * context arguments in their C++ code, I don't expect
    to see people using tr1::function for this problem anytime soon.

    The bottom line for me is that anytime you find yourself saying
    "callback" or typing "void *" in your C++ code, you should pause and
    consider if you're being a stupid C programmer while calling yourself
    a smart C++ programmer.
    --
    "The Direct3D Graphics Pipeline" -- DirectX 9 draft available for download
    <http://legalizeadulthood.wordpress.com/the-direct3d-graphics-pipeline/>

    Legalize Adulthood! <http://legalizeadulthood.wordpress.com>
    Richard, Feb 11, 2010
    #10
    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. JFCM
    Replies:
    4
    Views:
    5,711
  2. CoolPint
    Replies:
    8
    Views:
    944
    Jeff Schwab
    Dec 14, 2003
  3. Dave
    Replies:
    3
    Views:
    353
    tom_usenet
    Aug 10, 2004
  4. hdixon
    Replies:
    3
    Views:
    617
    hdixon
    Jul 9, 2006
  5. lovecreatesbeauty
    Replies:
    43
    Views:
    1,274
    Keith Thompson
    Feb 6, 2006
Loading...

Share This Page