1

I need to wrap a callback mechanism of a C interface statelessly in C++ accepting callables, i. e. the wrapper class shall not have any non-const members. In the C interface (not under my control) there is a function to add a callback:

void c_add_callback(void(*callback)(void* context), void* context)

Here is my attempt to wrap the C interface in a class accepting a reference to a callable:

class CppWrapper
{
public:
    // C++ callable parameter
    template<typename Function>
    void AddCallback(Function& function)
    {
        c_callbacks_.push_back
            ([](void* context)
                {
                    (*reinterpret_cast<Function*>(context))();
                }
            );
        c_add_callback
            (c_callbacks_.back()
            , reinterpret_cast<void*>(&function)
            );
    }
private:
    // storage of c callbacks
    std::list<void(*)(void*)> c_callbacks_;
};

Obviously, this approach is not stateless, as I have to store the callbacks (member c_callbacks_) as passed to the C interface, otherwise the lambdas as passed to c_add_callbacks would be temporaries and would be destructed when c_add_callback returns, see Casting a universal reference to a callable to void pointer and vice versa

Is there a way to implement the wrapper class stateless, i. e. get rid of the member?

Please see https://onlinegdb.com/rkeh-UNX0U for the full code example.

Some background: The intention to make the wrapper stateless comes from the fact that the library behind the C interface may at some point dispose all added callbacks. Obviously this would lead to an ever growing list (member c_callbacks_) when cyclically adding callbacks and triggering the library to dispose them. How can I cope with such leak if the dispose action is not propagated over the C interface?

Lorenz Zhao
  • 242
  • 1
  • 11

1 Answers1

1

I'm not at all understanding what you are trying to achieve with c_callbacks_. You only use it for c_callbacks_.back(), directly after c_callbacks_.push_back()

Just use

c_add_callback([](void* context)
    {
       (*reinterpret_cast<Function*>(context))();
    },
    reinterpret_cast<void*>(&function));

Note: Like your example, this assumes the lifetime of &function is managed externally.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Added reason for the list in the question. In your version the lambda is a temporary and it's scope is limited to the call of c_add_callback, as far as I understand. I'd expect that this leads to a segmentation fault when executing the callback. Anyway your code does not lead to a segmentation fault during execution of the callbacks. I'm confused now. Can you explain why the lambda obviously is still valid during execution? – Lorenz Zhao Jun 26 '20 at 11:19
  • @LorenzZhao: The lambda is a temporary, yes. But the resulting function pointer points to a function, and there's no such thing as a "temporary function" in C++. – MSalters Jun 26 '20 at 11:23
  • Thanks for clearing this up. So I accept your solution. Here is the final version: https://onlinegdb.com/BJ9llPmRU – Lorenz Zhao Jun 26 '20 at 11:47