f



Adapting a C-style callback mechanism to C++

I'm using a C API (it's actually Spidermonkey's jsapi) with which I
can register callback functions:

void registerCallback(const char * name, CStyleFunctionPtr callback);

I'm trying to wrap and abstract away this API from my users by
offering a pure C++ interface, in which users could implement callback
via functions objects, or something similar. e.g.:

struct FooCallback : public CallbackFunction
{
	virtual void run() { /* ... /* }
}

void registerCppCallback(CallbackFunction * pCB);

You get the point. This way my callbacks have state, I don't need to
mess around with function pointers, my users don't have to even
#include the C-API's headers and I can translate their arguments from
the underlying API's C types to their equivalent STL classes.

My plan was to implement this by creating one "master" callback the C-
way and have it act as a dispatcher to the actual callback
implementations in C++.

The problem is that, with the C API I'm working, the callback
function's signature doesn't specify which function is being called.
So if I register more than one callback with the same master callback
function I have no way of knowing how to dispatch the call. So I'm
stuck basically.

The above code is a simplified version of the actual C interface I'm
working with. If you're interested, the actual API is jsapi (Mozilla's
implementation of Javascript) and what I'm trying to do is use the
JS_DefineFunction call to bridge between Javascript and C++ by
allowing Javascript code to call C++ functions/functors.


-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
7/18/2007 7:00:55 AM
comp.lang.c++.moderated 10738 articles. 1 followers. allnor (8506) is leader. Post Follow

2 Replies
465 Views

Similar Articles

[PageSpeed] 41

assaf <assaflavie@gmail.com> wrote in news:1184758436.991084.129780
@z24g2000prh.googlegroups.com:

> I'm using a C API (it's actually Spidermonkey's jsapi) with which I
> can register callback functions:
> 
> void registerCallback(const char * name, CStyleFunctionPtr callback);
> 
> I'm trying to wrap and abstract away this API from my users by
> offering a pure C++ interface, in which users could implement callback
> via functions objects, or something similar. e.g.:
> 
> struct FooCallback : public CallbackFunction
> {
>      virtual void run() { /* ... /* }
> }
> 
> void registerCppCallback(CallbackFunction * pCB);
> 
> You get the point. This way my callbacks have state, I don't need to
> mess around with function pointers, my users don't have to even
> #include the C-API's headers and I can translate their arguments from
> the underlying API's C types to their equivalent STL classes.
> 
> My plan was to implement this by creating one "master" callback the C-
> way and have it act as a dispatcher to the actual callback
> implementations in C++.
> 
> The problem is that, with the C API I'm working, the callback
> function's signature doesn't specify which function is being called.
> So if I register more than one callback with the same master callback
> function I have no way of knowing how to dispatch the call. So I'm
> stuck basically.

I'm a little unclear what you mean here.  Wouldn't you be registering
by name?

> The above code is a simplified version of the actual C interface I'm
> working with. If you're interested, the actual API is jsapi (Mozilla's
> implementation of Javascript) and what I'm trying to do is use the
> JS_DefineFunction call to bridge between Javascript and C++ by
> allowing Javascript code to call C++ functions/functors.
> 
> 

Have a look at Boost/TR1 function:
http://www.boost.org/doc/html/function/tutorial.html#id1186501
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1402.html

Your storage could be something like std::map<std::string,
boost::function<...> > if your functions have the same signature.  The
function objects are typesafe and can store all sorts of functions,
including function pointers, functors, and binders.

The JSNative function pointer type was not immediately availabe in a
search, so you'll have to match that.

You could have something like this:
class CallbackMgr
{
    typedef CallbackT boost::function<int(int)>; //whatever JSNative is
    typedef ContainerT std::map<
        std::string,
        CallbackT>;
    ContainerT container;
public:
    // returns true if replacing old value
    bool register(const char * name, CallbackT callback)
    {
        return container.insert(std::make_pair(
            std::string(name),
            callback)).second;
    }
    int execute(const char * name, int arg)
    {
        ContainerT::const_iterator i = container.find(std::string(name));
        if (i != container.end) return i->second(arg);
        // do not-found error processing here...
    }
};

There's a minor drawback: std::string is kind of heavy as a key, so
you might want to look at yasli or Alexandrescu's article on maps with
expensive keys.  I doubt you really care.

You'll probably want to wrap all the various types for your own
sanity...

Generally if you have separable or fine-grained callback needs, then
Boost/TR1 function is the way to go.  If you have callbacks grouped
together logically and they all should be implemented, then it's
better to group them in a class having virtual functions:

// user must inherit and implement, see Non-Virtual Interface
class CallbackGroup
{
public:
    void f1() { f1Imp(); }
.....
    void fn() { fnImp(); }
    virtual ~CallbackGroup() = 0 { }
private:
    virtual void f1Imp() = 0;
.....
    virtual void fnImp() = 0;
};

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
W
7/18/2007 3:28:38 PM
assaf wrote:
> I'm using a C API (it's actually Spidermonkey's jsapi) with which I
> can register callback functions:
> 
> void registerCallback(const char * name, CStyleFunctionPtr callback);

Two questions here:
1. How does 'CStyleFunctionPtr' look like?
2. What is 'name' used for?

> I'm trying to wrap and abstract away this API from my users by
> offering a pure C++ interface, in which users could implement callback
> via functions objects, or something similar. e.g.:
> 
> struct FooCallback : public CallbackFunction
> {
> virtual void run() { /* ... /* }
> }
> 
> void registerCppCallback(CallbackFunction * pCB);
> 
> You get the point.

Ahem, not really: my first question would be who owns 'pCB' afterwards.

> This way my callbacks have state, I don't need to 
> mess around with function pointers,

...instead you mess around with object pointers with unclear ownership?
Seriously, consider either using references or use auto_ptr to make
ownership clear.

> my users don't have to even 
> #include the C-API's headers and I can translate their arguments from
> the underlying API's C types to their equivalent STL classes.
> 
> My plan was to implement this by creating one "master" callback the C-
> way and have it act as a dispatcher to the actual callback
> implementations in C++.
> 
> The problem is that, with the C API I'm working, the callback
> function's signature doesn't specify which function is being called.
> So if I register more than one callback with the same master callback
> function I have no way of knowing how to dispatch the call. So I'm
> stuck basically.

Okay. Typically, a C-style callback interface takes a function pointer and a
void pointers that holds some user-defined context. In you case, it could
only be the 'name', therefore also my initial two questions. If no such
thing is provided, there is nothing you can do. Otherwise, you could either
use the context to map to the object making the request or make the context
a pointer to the object itself.

Uli


-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Ulrich
7/19/2007 8:55:15 AM
Reply: