0

I have a class, Component, which has to interact with C code.

//// Component.h file   ////
class Component{
public:
    uint8_t getdevice1Value();
    void setdevice1Value(uint8_t value);

    uint8_t getdevice2Value();
    void setdevice2Value(uint8_t uint8_t);
private:
    uint8_t device1Value;
    uint8_t device2Value;
}

The object of the class would be created when its relevant thread is created in some Application.cpp file:

///////Some function where the Component is used//////
createThread(){
    Component myComponent;  // Scope within the thread
    // Some actions
}

Now comes my C code, which happens to be event driven. Within these functions, I would like to link my Class methods:

//// device1_event.c file   ////
void command_get_device_value()
{
    // code
    // assign variable  = Component::getdevice1Value() function
    // code
}

void command_set_device_value()
{
    // code
    // call Component::setdevice1Value(variable)  passing some variable
    // code
}

Similar to device1_event.c file, I have another device2_event.c where I would like to map the function calls to getdevice2Value and setdevice2Value.

I looked at the questions Using a C++ class member function (cannot be static) as a C callback function or also this Pass a C++ member function to a C function, where a struct registers the context and the function pointers.

I have a constraint in my case of not being able to dynamic allocation. So, I cannot use the new operator.

Now I have a few questions regarding these:

  1. Is the callback concept applicable in my case?
  2. If the first question is a yes, then:
    • How do I go about implementing it. I am a bit confused about this. I mean the call-functions need to be placed within the C-functions and also I need to register them once the Component instance is created. How can I exactly do this?
    • How do I bring the callback functions to my C files?
  3. In this question a struct was employed. Where do I declare the 'struct'? I did try declaring it in the Component.h file and introduced it as an extern within the device1_event.c file. But I get an incomplete type error.
mmcblk1
  • 96
  • 2
  • 9
  • 1
    You are focusing too much on things that don't apply. `new` and `struct` have nothing to do with this. All you need to know is that the C code needs to accept a function pointer and a user-defined value, where it passes the value as a parameter whenever it calls the function. Then the C++ code can pass in a standalone function or a class `static` method for the function pointer, and a pointer to the `Component` object as the user-defined value. That way, the function/method can access the `Component` object. – Remy Lebeau Apr 13 '20 at 20:59
  • 1
    No two C++ programs are alike. Attempting to look at other, random C++ programs, and copy-paste their approach to interface with C libraries, without having a complete understanding of what's going on, is a recipe for frustration, and failure. In order to invoke a method on a C++ object you need not just the pointer to a class method, but a pointer to the object whose method to be called. Full stop. That's it. Nothing else to say. How you go about making this happen for a callback from a C library, you can do it in whichever way you like, as long as those requirements are met. – Sam Varshavchik Apr 13 '20 at 20:59
  • @RemyLebeau I am not sure as to how do I get the function pointers to the instance of `myComponent` when it is actually created. Also, what confuses me is how do I go about with making a link between the function pointer for that (or `this` instance) and the call within the C code. – mmcblk1 Apr 13 '20 at 21:04
  • @SamVarshavchik My intention was not to copy code and then post a question. I do understand the fact that the `C` code, needs a pointer to the member class functions. The instance is created within some function call, `createThread()`, and I am not able to figure out how do get the address of this instance, and then pass it on the C function. – mmcblk1 Apr 13 '20 at 21:08
  • @mmcblk1 -- *how do I go about with making a link between the function pointer for that (or this instance) and the call within the C code.* -- That is a design issue, believe it or not. The instance could be a static variable somewhere, or could be passed as a "user data" value (if your API supports it), or any number of other ways not mentioned. Whatever you choose, it will probably be ok, as long as it works for you. What you are linking to are basically "home-made" ways of doing this -- nothing stops you from coming up with your own method. – PaulMcKenzie Apr 13 '20 at 21:11
  • *I did try declaring it in the Component.h file and introduced it as an extern within the device1_event.c file. But I get an incomplete type error.* Well your `device1_event.c` definitely needs a `#include "Component.h"` – Ben Voigt Apr 13 '20 at 21:17
  • @BenVoigt sorry about missing that out, but I indeed did that. I guess, I have something else wrong there. – mmcblk1 Apr 13 '20 at 21:19
  • You're also missing a semicolon after the class definition. That could cause something below it to be hidden from the compiler. – Ben Voigt Apr 13 '20 at 21:21
  • @BenVoigt: This just happens to be an MWE. For now I have the class compiling. Also, I have used some global variables, and I pass them between the `C` and the `C++` code. – mmcblk1 Apr 13 '20 at 21:30
  • It's not an MWE -- if there are missing #includes and missing semicolons, it is not working. – Ben Voigt Apr 13 '20 at 21:37

1 Answers1

1

The classical C way of passing callbacks is to pass two values: a pointer to the callback itself, and an opaque pointer which will be passed to the callback as an additional argument (take a look at qsort_r for example). When interfacing with C++, that opaque value may be used as instance pointer; you will only need to write a thin wrapper:

class B {
    void my_callback(int arg);
    static void my_callback_wrapper(int arg, void *u) {
        ((B*)u)->my_callback(arg);
    }
};

// or even:
extern "C" void my_callback_wrapper(int arg, void *u) {
    ((B*)u)->my_callback(arg);
}

and pass a pointer to the wrapper, along with a pointer to the object, to the C part. Be careful to use the exact same class type on both sides and not base/derived classes, for example.

Note that while it may be possible to get a pointer to the (non-static) method itself, on some compilers (tested on MSVC a long time ago) they have a special calling convention so that the pointer will not be compatible with any normal function pointer.

numzero
  • 1,180
  • 4
  • 6
  • Ok, and then if I am not wrong, I can pass the pointer from my `C++` method to the to `C` file, as a global variable? – mmcblk1 Apr 14 '20 at 09:25
  • Yes you can (as a void pointer), but unless your class is a singleton, it’s way better to pass it as a function argument (still void pointer). – numzero Apr 14 '20 at 18:19
  • I decided to go the `my_callback_wrapper` way! – mmcblk1 Apr 14 '20 at 19:30