Mixing C library code and callbacks with C++ Classes

Discussion in 'C++' started by womanontheinside@yahoo.com, Dec 7, 2004.

  1. Guest

    I have a library which was written in C, you call a function, it
    provides the result by a callback to specific function names. I am
    trying to wrap the calls to this inside a class, but this causes a
    problem with the callbacks, for example:

    class X {
    public:
    add();
    };

    X::add() {
    library_add(1,2);
    }

    library_add will return the result by calling add_result(int) callback.
    I need this function inside the class, but then it cannot be seen, and
    I get linker errors; I have to have the add_result outside of the
    class, but then it has no access to the internals of the class.

    Ideally, it would be like this

    class X {
    public:
    add();
    add_result();
    private:
    sum;
    };

    X::add() {
    library_add(1,2);
    }

    X::add_result(int a) {
    sum+=a;
    }

    /*
    In reality, I have to do this but then I can
    only have one instance of the object working

    X *pointer_to_an_x_instance;
    X::X() {
    pointer_to_an_x_instance = this;
    }

    //add_result is NOT a member method
    add_result(int a) {
    pointer_to_an_x_instence->inc_sum(a);
    }
    */

    Obviously this is just a little example I made up, rather than
    involving the whole complexities of the real library.

    Can somebody explain if, and how I can achieve it and be fully object
    oriented.
     
    , Dec 7, 2004
    #1
    1. Advertising

  2. Billy Patton Guest

    On Tue, 7 Dec 2004 wrote:

    > I have a library which was written in C, you call a function, it
    > provides the result by a callback to specific function names. I am
    > trying to wrap the calls to this inside a class, but this causes a
    > problem with the callbacks, for example:
    >
    > class X {
    > public:
    > add();
    > };
    >
    > X::add() {
    > library_add(1,2);
    > }
    >
    > library_add will return the result by calling add_result(int) callback.
    > I need this function inside the class, but then it cannot be seen, and
    > I get linker errors; I have to have the add_result outside of the
    > class, but then it has no access to the internals of the class.
    >
    > Ideally, it would be like this
    >
    > class X {
    > public:
    > add();
    > add_result();
    > private:
    > sum;
    > };
    >
    > X::add() {
    > library_add(1,2);
    > }
    >
    > X::add_result(int a) {
    > sum+=a;
    > }
    >
    > /*
    > In reality, I have to do this but then I can
    > only have one instance of the object working
    >
    > X *pointer_to_an_x_instance;
    > X::X() {
    > pointer_to_an_x_instance = this;
    > }
    >
    > //add_result is NOT a member method
    > add_result(int a) {
    > pointer_to_an_x_instence->inc_sum(a);
    > }
    > */
    >
    > Obviously this is just a little example I made up, rather than
    > involving the whole complexities of the real library.
    >
    > Can somebody explain if, and how I can achieve it and be fully object
    > oriented.
    >
    >


    did you do for your c routines
    #ifdef __cplusplus
    extern "C" {
    #endif
    #include <stdio.h>
    #include "my_c_routines.h"
    #ifdef __cplusplus
    }
    #endif

    Put this in your c head files. I believe it prevents name mangling.

    ___ _ ____ ___ __ __
    / _ )(_) / /_ __ / _ \___ _/ /_/ /____ ___
    / _ / / / / // / / ___/ _ `/ __/ __/ _ \/ _ \
    /____/_/_/_/\_, / /_/ \_,_/\__/\__/\___/_//_/
    /___/
    Texas Instruments ASIC Circuit Design Methodology Group
    Dallas, Texas, 214-480-4455,
     
    Billy Patton, Dec 7, 2004
    #2
    1. Advertising

  3. Rade Guest

    <> wrote in message
    news:...
    >I have a library which was written in C, you call a function, it
    > provides the result by a callback to specific function names. I am
    > trying to wrap the calls to this inside a class, but this causes a
    > problem with the callbacks, for example:
    >

    [snip]

    First of all, I hope you have a good reason to wrap this C library with C++
    objects. Sometimes the C libraries have a good object-oriented design (at
    least at the interface level), just this design is expressed in a wrong
    language (C). If the design is apparent from the library itself, then it
    should be straightforward to perform the wrapping. You will recognize such a
    design, for example, if there are groups of functions, which are grouped
    around particular tasks, where majority of functions from a single group has
    something common in the parameters - some opaque pointer to a structure, a
    "handle" or something similar. Here is a couple of declarations from zlib,
    for example (comments omitted):

    ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
    ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
    ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
    ....
    ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
    ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
    ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
    voidpc buf, unsigned len));
    ....

    As a first attempt, one could wrap all inflate/deflate functions into one
    class (which will encapsulate z_streamp), while wrapping all gz... functions
    into another class (which will encapsulate gzFile). This may not produce a
    terribly good C++ code, but at least the overall class design of this code
    won't be worse that the object-oriented design of the original class
    library.

    Regarding the callbacks: as Greg have noticed, sometimes C libraries provide
    a "context" that you specify when registering a callback with the library
    (and which you receive in your callback). Similarly, the C library may
    return you a "cookie" while registering a callback. This is an additional
    sign that the C library creator had an OO design in mind. If it is missing,
    then this C library is probably not designed in an OO way.

    My point is, actually: look at the C library and try to understand its
    design. If it is object-oriented, you can try to wrap it into classes. If it
    isn't, you better leave the C library as it is, and directly use it in your
    C++ programs. IMHO it is easier for C++ programmers (at least for me!) to
    comprehend no object-oriented design from a C library than to comprehend a
    bad design from a C++ library.

    Finally, to avoid comments that I am talkative without saying anything... if
    you lack "context" supplied when registering callbacks, you still can
    develop a "connection point", i.e. a Singletone class that can distribute
    notifications (a degenerate case of Observer pattern). Something like:

    //==================================
    // library.h
    //==================================
    #ifndef LIBRARY_H
    #define LIBRARY_H

    extern "C"
    {
    void library_register_add(void (*callback)(int));
    void library_unregister_add();
    void library_add(int x, int y);
    }

    #endif /* LIBRARY_H */

    //==================================
    // AddResultCP.h
    //==================================
    #ifndef ADD_RESULT_CP_H
    #define ADD_RESULT_CP_H

    #include <set>

    class AddResultReceiver;

    class AddResultCP
    {
    public:
    static AddResultCP &Instance();

    void Register(AddResultReceiver *receiver) {
    receivers.insert(receiver); }
    void Unregister(AddResultReceiver *receiver) {
    receivers.erase(receiver); }

    private:
    AddResultCP() { library_register_add(AddResultCallback); }
    ~AddResultCP() { library_unregister_add(); }

    static void AddResultCallback(int result) {
    Instance().NotifyAddResult(result); }
    void NotifyAddResult(int result);

    typedef std::set<AddResultReceiver *> ReceiverSet;
    ReceiverSet receivers;
    };

    class AddResultReceiver
    {
    protected:
    AddResultReceiver() { Register(); }
    virtual ~AddResultReceiver() { Unregister(); }

    void Add(int x, int y) { library_add(x, y); }

    void Register() { AddResultCP::Instance().Register(this); }
    void Unregister() { AddResultCP::Instance().Unregister(this); }

    private:
    friend class AddResultCP;
    virtual void AddResult(int result) = 0;
    };

    #endif // ADD_RESULT_CP_H

    //==================================
    // AddResultCP.cpp
    //==================================

    AddResultCP &AddResultCP::Instance()
    {
    static AddResultCP cp;
    return cp;
    }

    void AddResultCP::NotifyAddResult(int result)
    {
    for (ReceiverSet::iterator it = receivers.begin(); it !=
    receivers.end(); ++it)
    {
    (*it)->AddResult(result);
    }
    }

    To use this approach, one should derive from AddResultReceiver and implement
    AddResult method. Then, she can call Add from the derived class and expect
    AddResult to be called when the library prepares the result. Of course, this
    is not a very polished C++ code, but it illustrates a possible approach.

    Beware of:

    (1) although Add is invoked on a single AddResultReceiver object, ALL
    registered AddResultReceiver objects will receive the result (this is
    actually a biggest problem in this approach, but sometimes you can't do
    better),
    (2) reentrancy (your AddResult method may be called in the middle of your
    Add method),
    (3) threading issues if you work on a multithreaded platform,
    (4) temptation to add much functionality to the above classes. They should
    serve just as an interface between C library and its C++ users - the real
    job is performed by the users.

    I don't know whether this suits your needs - I hope it may help.

    Regards,
    Rade
     
    Rade, Dec 7, 2004
    #3
  4. msalters Guest

    wrote:
    > I have a library which was written in C, you call a function, it
    > provides the result by a callback to specific function names.


    Be aware that this is /not/ what the rest of the world means when
    they talk about callbacks. What they mean is passing the /address/
    of a function /at runtime/.

    This means that the "stock C++ reply to callbacks" won't work here.
    ( which would be, use a static member function - won't work here)

    > I am trying to wrap the calls to this inside a class, but this
    > causes a problem with the callbacks, for example:
    >
    > class X {
    > public:
    > add();
    > };
    >
    > X::add() {
    > library_add(1,2);
    > }
    >
    > library_add will return the result by calling add_result(int)

    callback.
    > I need this function inside the class, but then it cannot be seen,

    and
    > I get linker errors; I have to have the add_result outside of the
    > class, but then it has no access to the internals of the class.


    Wrong assumption - friends are outside the class but can access
    the internals - provided they know /which/ object they have to look in.
    Same goes for any OO function, they all need objects. Usually on the
    left hand side of a . but that's just convention.

    > In reality, I have to do this but then I can
    > only have one instance of the object working
    >
    > X *pointer_to_an_x_instance;
    > X::X() {
    > pointer_to_an_x_instance = this;
    > }
    >
    > //add_result is NOT a member method
    > add_result(int a) {
    > pointer_to_an_x_instence->inc_sum(a);
    > }
    > */
    >
    > Obviously this is just a little example I made up, rather than
    > involving the whole complexities of the real library.


    Another solution is
    stack<X*> pointer_to_x_instances;
    X::add()
    pointer_to_x_instances.push(this);
    library_add(1,2);
    pointer_to_x_instances.pop();
    }
    //add_result is NOT a member method
    add_result(int a) {
    X* that = pointer_to_x_instances.top();
    that->do_add_result(a);
    }

    It's even reentrant with its own stack. Still, this is not
    really OO. There are just things that can't be fixed. This one
    qualifies, a bad C design makes even worse C++.
    Regards,
    Michiel Salters
     
    msalters, Dec 9, 2004
    #4
  5. Guest

    That looks like the kind of system I am going to use, although looking
    at it, I dont think I need that stack; as the whole thing is
    sequential, it would be simple enough to have a global pointer that is
    set before the library_add then unset afterwards, which is basically
    what is happening at the top of stack? (What I am trying to say, is
    that pointer_t_x_instances will never end up having more than 1
    instance).

    Thank you for all the ideas and information, it's been a great help.
    Regarding the C library, that was written by somebody else, it has no
    idea of registering contexts, from the limited documents it states that
    one is required to provide these functions with the specifc names, no
    options, they must just exist in the module.

    To me, object oriented code is the norm, and makes more sense, which is
    why I am trying to maintain something along those lines - the rest of
    my system is object oriented.
     
    , Dec 9, 2004
    #5
    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. Replies:
    1
    Views:
    556
    Greg Schmidt
    Dec 7, 2004
  2. Ivan Voras

    Mixing classes...

    Ivan Voras, Aug 19, 2004, in forum: Python
    Replies:
    1
    Views:
    300
    Hans Nowak
    Aug 20, 2004
  3. L.C. Rees
    Replies:
    3
    Views:
    395
    Scott David Daniels
    Feb 8, 2006
  4. Stefan Arentz

    Mixing Python and C classes in a module

    Stefan Arentz, Oct 9, 2007, in forum: Python
    Replies:
    6
    Views:
    347
    John Machin
    Oct 10, 2007
  5. snacktime
    Replies:
    1
    Views:
    121
    Nobuyoshi Nakada
    Sep 20, 2006
Loading...

Share This Page