Alternative To Members As Callbacks?

N

none

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.
 
T

tonydee

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
 
Q

qdii

 The problem (as warned in the FAQ) is that doing so would

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.
 
R

Richard

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

none <[email protected]> spake the secret code
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.
 
Ö

Öö Tiib

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

none <[email protected]> spake the secret code
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.
 
J

James Kanze

[...]
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.
 
R

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:

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.
 
T

Thomas J. Gritzan

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.
 
K

Keith H Duggar

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
 
R

Richard

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

"Thomas J. Gritzan" <[email protected]> spake the secret code
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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,818
Latest member
Brigette36

Latest Threads

Top