C ++ "Dynami c" указатели на функции для C функций обратного вызова - PullRequest
4 голосов
/ 17 марта 2020

У меня есть API для управления конфигурацией камеры. Есть 344 отдельных варианта управления. Когда определенное значение изменяется, API вызывает функцию обратного вызова для уведомления программы. Функция регистра принимает указатель функции

void RegisterCallback(Option * ptr, void (fn*)(void*))

в качестве функции обратного вызова. Я не могу использовать одну функцию для обратного вызова, потому что теперь я делаю, откуда происходит обратный вызов .

Одно из решений - создать 344 отдельных функции обратного вызова:

void callback0(void*);
void callback1(void*);
...
void callback{n-1}(void*);

static void(*)(void*) callbacks[] = {callback0, callback1, ..., callback{n-1}};

Для этого решения мне нужно было бы сгенерировать заголовок с помощью отдельного инструмента / скрипта.

Другое решение - использовать препроцессор magi c (BoostPP), например

BOOST_PP_FOR((0,1024), PRED, OP, MYSTERIOUS_CALLBACK_MACRO);

По моему опыту, эти макросы не читаются разработчиками и их сложно поддерживать.

В идеале я мог бы использовать что-то вроде RegisterCallback(popt, [n](void*){/*n-th callback*/});

Но лямбда-функция - это функтор, а не указатель на функцию.

Мой вопрос такой: могу ли я создавать эти функции динамически? Или есть лучшее решение для этой проблемы, чем два выше?

Спасибо.

РЕДАКТИРОВАТЬ

Я получил ответ из Ботье. Передача объекта в функцию обратного вызова требует от вас доступа к коду на двоичном уровне (помимо C / C ++). Если Вы можете разрешить это, то решение может быть libffi, поскольку оно генерирует указатель c на каждый экземпляр Вашей функции. Он широко поддерживается, но, например, компилятор Visual Studio отсутствует в списке.

РЕДАКТИРОВАТЬ 2 Как отмечали другие, он должен работать и с VS.

Ответы [ 2 ]

2 голосов
/ 18 марта 2020

Вы не предоставляете много подробностей об API камеры или семантике вызова; если это общедоступное программное обеспечение, будет полезна ссылка на актуальную документацию.

В любом случае я нахожу вопрос интересным и покажу ниже решение, которое использует тот факт, что мы можем генерировать различные функции с шаблонами вместо кодировать их вручную.

В приведенном ниже коде предполагается, что параметр Option *ptr в void RegisterCallback(Option *ptr, void (fn*)(void*)) является указателем, который вы получили с камеры; он служит индикатором для API камеры, для которого вы хотите зарегистрировать этот обратный вызов. Возможно, API камеры имеет перечисление с 344 членами или около того для этого, что заменит параметры шаблона size_t и тип ключа карты ниже.

Для регистрации обратного вызова для каждого из них вызывается простая рекурсивная функция шаблона. 344 варианта.

#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;
}

Пример сеанса:

$ 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
2 голосов
/ 17 марта 2020

Вы можете генерировать замыкания динамически, используя libffi . Это обычные указатели на функции, которые могут сохранять один указатель на пользовательские данные, в данном случае это номер обратного вызова.

#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 is callback #13 with argument 0xabcdef
This is callback #57 with argument 0x1234
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...