3
\$\begingroup\$

I am trying to callback C++ class methods from C. Now I have got something working, but am not sure if this is the best solution.

I am looking for feedback, as to if there is something wrong with this approach, and how could I make this better.

So, here goes my code:

This is the C++ class Foo. The class has the methods getValue() & setValue(), which I would like to like to be called-back from the C code:

Contents of Foo.h:

#ifdef __cplusplus
class Foo
{
public:
    Foo();
    int getValue() const;
    void setValue(int value);

private:
    int value_;
};
#endif

// Free functions
int  c_wrapper_getValue(void *arg1);
void c_wrapper_setValue(int value, void *arg1);

Contents of the source file, Foo.cpp:

#include "Foo.h"

Foo::Foo() :
    value_{0}
{}

int Foo::getValue() const
{
    return value_;
}

void Foo::setValue(int value)
{
    value_ = value;
}

void c_wrapper_setValue(int value, void *arg1)
{
    Foo* foo_instance = static_cast<Foo*>(arg1);
    foo_instance->setValue(value);
}

int c_wrapper_getValue(void *arg1)
{
    Foo* foo_instance = static_cast<Foo*>(arg1);
    return foo_instance->getValue();
}

Here is the code for the C files, which in turn call the Foo class members. First, the header Test.h file:

#include <stdio.h>
#include "Foo.h"

#ifdef __cplusplus
extern "C" {
#endif

// Callback function pointers
typedef int  (*get_handler_t)(void* arg1);
typedef void (*set_handler_t)(int foo, void* arg1);

void register_handler(get_handler_t g, set_handler_t s, void* p_instance);

// These are the functions which call the callback functions
int call_cpp_get_function();
void call_cpp_set_function(int value);

#ifdef __cplusplus
}
#endif

And finally the Test.c file:

#include "Test.h"

static get_handler_t get_handler_ = NULL;   /**< Getter function pointer of type int (*g)(void *). */
static set_handler_t set_handler_ = NULL;   /**< Setter function pointer of type void (*s)(int,  void *). */
static void* foo_object_instance  = NULL;    /**< Instance of Foo object. */

//! Registers the getter and setter function with the object of Class Foo
//! \param g Getter function pointer of type int (*g)(void *).
//! \param s Setter function pointer of type void (*s)(int,  void *).
//! \param p_instance Instance of Foo object.
void register_handler(
    int (*g)(void *),
    void (*s)(int,  void *),
    void* p_instance)
{
    get_handler_ = g;
    set_handler_ = s;
    foo_object_instance = p_instance;
}

//! Function calls the get function of class Foo
//! \return Value of value_ from class Foo
int call_cpp_get_function()
{
    return get_handler_(foo_object_instance);
}

//! Function calls the set function of class Foo
//! \param value Value to be set in class Foo
void call_cpp_set_function(int value)
{
    set_handler_(value, foo_object_instance);
}

Now I could create an object and call the functions present in my C file, which called back the C++ methods, like this:

Foo foo_object;
register_handler(&c_wrapper_getValue, &c_wrapper_setValue, static_cast<void *>(&foo_object));
call_cpp_get_function();
call_cpp_set_function(35);

I do have some constraints in my case, for example I can't use STL, since the code is supposed to run on an embedded system. So, how bad is my code?

\$\endgroup\$
1
\$\begingroup\$

Encapsulate stuff that shouldn't be public

I'd rather make the callback functions private static functions of the c++ class This helps to encapsulate the nasty void*casts:

#ifdef __cplusplus
class Foo {
public:
    Foo();
    int getValue() const;
    void setValue(int value);

    void register_callbacks();

private:
    int value_;

    static int  cb_getValue(void *arg1);
    static void cb_wrapper_setValue(int value, void *arg1);
};
#endif

Implementation:

void Foo::register_callbacks() {
    register_handler(Foo::cb_getValue, Foo::cb_setValue, static_cast<void *>(this));
}

void Foo::cb_setValue(int value, void *arg1) {
    Foo* foo_instance = static_cast<Foo*>(arg1);
    foo_instance->setValue(value);
}

int Foo::cb_getValue(void *arg1) {
    Foo* foo_instance = static_cast<Foo*>(arg1);
    return foo_instance->getValue();
}

Usage:

Foo foo_object;
foo_object.register_callbacks();
call_cpp_get_function();
call_cpp_set_function(35);
\$\endgroup\$
1
  • \$\begingroup\$ This seems much more neater! Thanks \$\endgroup\$ – mmcblk1 Jun 17 '20 at 21:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.