C++ “Dynamic” function pointers for C callback functions

前端 未结 2 1150
生来不讨喜
生来不讨喜 2021-01-18 21:44

I have an API for managing a camera configuration. There are 344 individual options to manage. When a certain value changes the API calls a callback function to notify the p

相关标签:
2条回答
  • 2021-01-18 22:13

    You can generate closures dynamically using libffi. These are regular function pointers that can retain one pointer to user data, in this case the callback number.

    #include <iostream>
    #include <vector>
    #include "ffi.h"
    
    using namespace std;
    void the_callback(size_t num, void* arg) {
        cout << "This is callback #" << num << " with argument " << hex << arg << endl;
    }
    
    void generic_cb(ffi_cif *cif, void *ret, void **args, void *user_data) {
        the_callback((size_t) user_data, *(void **)args[0]);
    }
    
    typedef void (*Callback)(void*);
    
    ffi_cif callback_cif;
    int main() {
        ffi_type *argtypes[] = { &ffi_type_pointer };
        ffi_status st = ffi_prep_cif(&callback_cif, FFI_DEFAULT_ABI, 1, &ffi_type_void, argtypes);
    
        std::vector<Callback> callbacks;
        for (size_t i = 0; i < 100; i++) {
            void *cb;
            ffi_closure *writable = (ffi_closure*)ffi_closure_alloc(sizeof(ffi_closure), &cb);
            st = ffi_prep_closure_loc(writable, &callback_cif, generic_cb, (void*)i, cb);
            callbacks.push_back((Callback)cb);
        }
    
        callbacks[13]((void*)0xabcdef);
        callbacks[87]((void*)0x1234);
    }
    

    This produces the following output:

    This is callback #13 with argument 0xabcdef
    This is callback #57 with argument 0x1234
    
    0 讨论(0)
  • 2021-01-18 22:18

    You do not provide many details about the camera API or the semantics of the call; if it is publicly available software a link to the actual documentation would be helpful.

    In any case I found the question interesting and will show a solution below which uses the fact that we can generate different functions with templates, instead of coding them by hand.

    The code below assumes that the Option *ptr parameter in void RegisterCallback(Option *ptr, void (fn*)(void*)) is a pointer which you obtained from the camera; it serves as an indicator to the camera API for which option you want to register this callback. Conceivably the camera API has an enumeration with 344 members or such for this, which would replace the size_t template parameters and map key type below.

    A simple recursive template function is called to register a callback for each of the 344 options.

    #include <cstdio>
    #include <map>
    #include <cassert>
    using namespace std;
    
    typedef void (*voidFnPtrT)(void *);
    
    ////////////////////////////////////////////////////////////
    // This part simulates the camera API.
    class Camera
    {   public:
        static constexpr size_t NUM_OPTS = 344;
        static constexpr size_t OPTSIZE = 100;
        struct Option
        { 
            unsigned char data[OPTSIZE];
        };
    
        Option &GetOption(size_t index) { return options[index]; }
    
        void RegisterCallback(Option *opt, voidFnPtrT callback) 
        { 
            callbacks[opt] = callback;
        }
    
        /// Set a new option value and call the callback,
        /// if set for that option.
        void SetOptionVal(size_t index, Option *newVal) 
        {
            assert(index < NUM_OPTS);
            options[index] = *newVal;
            OnOptionChange(index);
        }
        private:
        Option options[NUM_OPTS];
        map<Option *, voidFnPtrT> callbacks;
    
        /// If there is a callback registered 
        /// with the option at that index, call it.
        void OnOptionChange(size_t index) 
        { 
            auto iter = callbacks.find(options + index); 
            if(iter != callbacks.end())
            {
                (*iter->second)(iter->first);
            }
        }
    };
    //////////// End API simulation ///////////////////////////////
    
    /// A single user function which can serve as a  
    /// callback for any property change. The property is 
    /// identified through the index (could be an enum
    /// or any other compile time constant).
    void singlebigcallback(size_t index, void *data)
    {
        // This is still awkward, but in some location we must code 
        // what happens for each option. In terms of lines of code
        // it is not much advantageous to simply putting each switch 
        // case contents directly into a specialization of callbackTemplFn.
        // In terms of maintainability that may actually be even better.
        // Anyway, you wanted a single function, here it is.
    
        switch(index)
        {
            // obviously this demo code does not need a switch but can 
            // be handled by assembling a string, but imagine some distinct 
            // activity in each case.
            case 0:  printf("reacting to change in property 0\n"); break;
            case 1:  printf("reacting to change in property 1\n"); break;
            case 2:  printf("reacting to change in property 2\n"); break;
            default: printf("property callback for %zu not yet implemented\n", index); break;
        }
    }
    
    /// A template with a number template parameter.
    /// The signature of each instantiation is 
    /// void ()(void *) and hence can be used 
    /// as a callback function for the camera.
    template<size_t N> void callbackTemplFn(void *data)
    {
        singlebigcallback(N, data);
    }
    
    /// This function registers the proper callbackTemplFn
    /// for the given N and then recursively calls itself
    /// with N-1.
    template<size_t N> void RegisterAllCallbacks(Camera &cam)
    {
        cam.RegisterCallback(&cam.GetOption(N), &callbackTemplFn<N>);
        RegisterAllCallbacks<N-1>(cam);
    }
    
    /// The recursion end: There is no index smaller than 0,
    /// so we register the proper function and return.
    template<> void RegisterAllCallbacks<0>(Camera &cam)
    {
        cam.RegisterCallback(&cam.GetOption(0), &callbackTemplFn<0>);
        // No further recursion.
    }
    
    
    int main()
    {
        Camera camera;
        Camera::Option opt; // Normally one would put some data in there.
        RegisterAllCallbacks<Camera::NUM_OPTS-1>(camera);
    
        camera.SetOptionVal(0, &opt);
        camera.SetOptionVal(2, &opt);
        camera.SetOptionVal(Camera::NUM_OPTS-1, &opt);
        return 0;
    }
    

    Sample session:

    $ g++ -Wall -o callbacks callbacks.cpp  && ./callbacks
    reacting to change in property 0
    reacting to change in property 2
    property callback for 343 not yet implemented
    
    0 讨论(0)
提交回复
热议问题