1

The Settings: I'm building an architecture that has parts in C and part in C++.

In my Architecture I have:

  1. A data_io(C) which gets data sends it to a processor callback and outputs the processed data.
  2. A data_processor(C) which takes care of processing data and changes on-demand.
  3. A settings_manager(C++) which decides which processor to use.

The relationship is as follows:

  1. The settings_manager object is instantiated, inside it initializes the data_processor with a default processor function, and then initializes the data_io sending to it a processing_callback_function (defined in the settings_manager, but internally referencing a data_processor function) which is then used to process the data when the data_io starts. (so, the data_io receives the processing_callback_function only once at initialization and does not care about what the callback does inside, it just generates data, calls the processing_callback_function and outputs processed data)
  2. While the system is working, the settings_manager may decide to change the data_processor type of processing, so it changes the processing_callback_function to call a different data_processor function.(the data_io does not know about it and continues working)

Here is the basic implementation:

data_io.c:

typedef void (* ProcessorCallback_t)(float *, int);
ProcessorCallback_t processorCallback;

data_io_init(ProcessorCallback_t callback) {
   processorCallback;
   ...init all other stuff like data pointer...
}

data_io_loop() {
    bufSize = readData(&data); 
    processorCallback(&data, bufSize);
    writeData(&data, bufSize);
}

data_procesor.c:

void data_processor_init() {
    ...initialization routine...
}

void data_processor_proc1(float *data, int bufSize) {
    ...data process...
}

void data_processor_proc2(float *data, int bufSize) {
    ...data process...
}

settings_manager.cpp:

void SettingsManager::start() {
    data_processor_init();
    this->processing_function = &data_processor_proc1;
    //THIS IS MY QUESTION! HOW TO PASS THE processing_callback_function TO data_io_init
    data_io_init(&SettingsManager::processing_callback_function); 
    ... here starts a new thread to run loop below... 
    //while(this->condition) { 
    //    data_io_loop();
    //}
}

void SettingsManager::changeProcessorType(int processorType) {
    switch(processorType) {
        case 1:
            this->processing_function = &data_processor_proc1;
            break;
        case 2:
            this->processing_function = &data_processor_proc2;
            break;
    }
}

void SettingsManager::processing_callback_function(float *data, int buffer_size) {
    this->processing_function(data, buffer_size);
}

My Questions:

1. How should I pass the processing_callback_function C++ member function to the data_io_init C function?

  • when I do the following:

    data_io_init(&SettingsManager::processing_callback_function);

I get the following error:

"Incompatible pointer types 'ProcessorCallback_t' and 'void(Engine::*)(float*,int)'"

Well the error is obvious, the types are different as the second is a member function and is part of the instance of the object.

I've read that I should make the processing_callback_function static, but I'm not sure if it is the right approach.

  1. What is the appropriate way to handle this kind of things, are there any patrons that might be useful to read, or coding strategies that may be related?
7
  • 3
    parts in C (for best performance with no class conversions or multiple time-consuming memory parsing) your assumptions about C++ are wrong. Commented Aug 3, 2017 at 19:25
  • How are you compiling these files, and what is the definition of SettingsManager? Commented Aug 3, 2017 at 19:27
  • @manni66 please explain I'm eager to learn, may answer my second question Commented Aug 3, 2017 at 19:28
  • 2
    "C++ too slow for time-cricital parts" - now that's hard core. Commented Aug 3, 2017 at 19:31
  • 2
    @alexm Since C is almost a subset of C++, C code can't be faster than carefully written C++ (unless you use different compilers). In worst case, the performance would be the same. To make your life easier, you could compile everything as C++. Commented Aug 3, 2017 at 19:48

2 Answers 2

2

A non-static class method has a hidden this pointer that a free-standing function does not. So such, you can't pass a non-static class method where a free-standing function is expected.

The best solution in this situation is to allow the C++ code to pass a user-defined value to the C code, and then have the C code passes that value back to the C++ code. That way, the C++ code can pass around its this pointer. For example:

data_io.h:

typedef void (* ProcessorCallback_t)(float *, int, void *);

void data_io_init(ProcessorCallback_t callback, void *userdata);
void data_io_loop();

data_io.c:

ProcessorCallback_t processorCallback;
void *processorUserData;

void data_io_init(ProcessorCallback_t callback, void *userdata) {
   processorCallback = callback;
   processorUserData = userdata;
   ...init other stuff as needed ...
}

void data_io_loop() {
    ...
    processorCallback(&data, bufSize, processorUserData);
    ...
}

Then SettingsManager can do this:

settings_manager.h:

typedef void (* ProcessorFunc_t)(float *, int);

class SettingsManager
{
private:
    ProcessorFunc_t processing_function;
    ...
    static void processing_callback_function(float *data, int buffer_size void *userdata);

public:
    void start();
    ...
};

settings_manager.cpp:

#include "data_io.h"

void SettingsManager::start()
{
    ...
    data_io_init(&SettingsManager::processing_callback_function, this); 
    ...
}

void SettingsManager::processing_callback_function(float *data, int buffer_size void *userdata)
{
    static_cast<SettingsManager*>(userdata)->processing_function(data, buffer_size);
}

Or this (as the above is technically undefined behavior, but it does work in most compilers. The below is more standards compliant):

settings_manager.h:

typedef void (* ProcessorFunc_t)(float *, int);

class SettingsManager
{
    ...

public:
    ProcessorFunc_t processing_function;

    void start();
    ...
};

settings_manager.cpp:

#include "data_io.h"

void processing_callback_function(float *data, int buffer_size void *userdata)
{
    static_cast<SettingsManager*>(userdata)->processing_function(data, buffer_size);
}

void SettingsManager::start()
{
    ...
    data_io_init(&processing_callback_function, this); 
    ...
}
Sign up to request clarification or add additional context in comments.

Comments

1

1) Write everything in C++. Use std::function as a callback. This is the best way to handle this. Do you really have a performance issue? Have you measured it? C++ is not that slow. Problems that you will have by mixing two lanuage styles will bring more problems.

2) Functions are the same in C and C++. You can always do the following:

class Cls {
public:
    void callback() {}
};

void Cls_callback(void* ptr) {
    reinterpret_cast<Cls*>(ptr)->callback();
}

And then pass that Cls_callback as the C callback.

3) You may make Cls_callback a static method of Cls and that way it will have access to private members of Cls:

class Cls {
public:
    static void Cls_callback(void* ptr) {
        Cls* self = reinterpret_cast<Cls*>(ptr);
        self->i += 1;
    }
private:
    int i;
};

But keep in mind, that this actually is an UB. It will work, it is slightly less code than variant 2, but technically speaking, standard does not guarantee that this will work.

P.S. Yet again, don't mix styles. Use C++ everywhere.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.