1

I'm using a function of an external library written in C (library_function) that takes a callback function as an argument. The parameters the library calls the callback function with don't matter to me. The only thing I'm interested in is the order of the calls to the callback function. I want the callback function to return the next element of a list every time it's called.

Consider this example:

#include <iostream>

int x;
bool *booleans;

void library_function(bool(*callback_fn)()){
    for (int i=0; i < 5; i++) {
        std::cout << callback_fn() << std::endl;
    }
}

bool my_callback_fn(){
    return booleans[x++];
}

void my_function(bool b[]){
    x = 0;
    booleans = b;

    library_function(my_callback_fn);
}

int main() {
    bool booleans[] {true, false, true, true, false};
    my_function(booleans);
}

This code works, but I had to use global variables which I think isn't good style.

In Python for example, I would use an inner function to achieve this:

def library_function(callback_fn):
    for _ in range(5):
        print(callback_fn())


def my_function(b):
    x = -1

    def my_callback_fn():
        nonlocal x
        x += 1
        return b[x]

    library_function(my_callback_fn)

if __name__ == '__main__':
    booleans = [True, False, True, True, False]
    my_function(booleans)

I've read that C++ doesn't support nested functions and that lambda functions can only be used as function pointers if they do not capture variables.

Is there a way to avoid using global variables but still being able to modify the 'internal state' of the callback function?

Felix
  • 6,131
  • 4
  • 24
  • 44
  • 1
    You could use a static function with the static variable. – Ari0nhh Jun 28 '16 at 09:39
  • Put state into classes an keep the context. – πάντα ῥεῖ Jun 28 '16 at 09:43
  • @Ari0nhh: I tried that, but I don't see a chance to reset `i` to `0` when `my_function` is called multiple times. If you know a way to do that, please feel free to post an answer. @πάνταῥεῖ: Thank you, I tried that and it worked. I posted the solution below. – Felix Jun 28 '16 at 10:56
  • 1
    *"The parameters the library calls the callback function with don't matter to me"* - it should. Often, when libraries allow to pass context to callbacks, they to that by additional parameters passed to callbacks. – el.pescado - нет войне Jun 28 '16 at 11:03

3 Answers3

1

I'm using a function of an external library written in C (library_function) that takes a callback function as an argument.

Unless the external library allows you to pass a user defined context like providing a callback signature like

bool(*callback_fn)(void*)
                // ^^^^^

Is there a way to avoid using global variables but still being able to modify the 'internal state' of the callback function?

there's no other choice than using a global context.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
0

Thanks to πάντα ῥεῖ's comment, I wrote a class that holds the state of my callback function:

#include <iostream>

void library_function(bool(*callback_fn)()){
    for (int i=0; i < 5; i++) {
        std::cout << callback_fn() << std::endl;
    }
}

class CallbackClass
{
    static bool *booleans;
    static int i;

    static bool my_callback_fn(){
        return booleans[i++];
    }

public:
    static void my_function(bool b[]){
        i = 0;
        booleans = b;
        library_function(my_callback_fn);
    }
};

int CallbackClass::i {};
bool* CallbackClass::booleans {nullptr};


int main() {
    bool booleans[] {true, false, true, true, false};
    CallbackClass::my_function(booleans);
}

This works well for me.

Community
  • 1
  • 1
Felix
  • 6,131
  • 4
  • 24
  • 44
-1

umm... it's may help you

void fn()
{
//tip: you can't use a struct in function to be a template argument.
    struct _function
    {
     static void foo()
        {
        }
    };
}

if you have a c++11 support compiler, you can do that

void(*)(int) fn()
{
    return [&](int){code block};// for cast it to a function pointer you can not capture any variable in code block.
}
isuedod
  • 1
  • 3