Обратный вызов C-Style для лямбда [дубликат]

В одной кавычки строка не содержит переменных внутри нее. Строка с двойными кавычками.

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

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

3
задан Praetorian 26 August 2014 в 20:35
поделиться

3 ответа

Лямбда, которая захватывает переменные контекста, не может быть преобразована в указатель на голой функции, поскольку это может привести к невозможности переноса захваченного состояния. То, что вы показали в примере, - это правильный способ решить проблему вызова функций-членов C ++ с помощью обратного вызова C.

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

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, 
                 [](char *ptr, size_t size, size_t nmemb, void *userdata) {
                    // invoke the member function via userdata
                    auto p = static_cast<MyClass *>(userdata);
                    return p->actualCallback(ptr, size, nmemb, userdata);
                 });

Обратите внимание, что вам, вероятно, следует избавиться от последнего параметра actualCallback(), так как это только указатель this, который не нужно передавать нестатической функции-члена явно.

2
ответ дан Praetorian 25 August 2018 в 18:45
поделиться

Вот что я хотел бы сделать (, пожалуйста, обратите внимание, что я достаточно знаю о C ++, чтобы сдуть случайную ногу ):

#include <iostream>
#include <functional>

void external_c_function(void cb(void *), void *userdata)
{
    cb(userdata);
}

void c_callback_wrapper(void *userdata)
{
    auto &lambda = *static_cast< std::function<void(void)>* >(userdata);
    std::cout << "calling lambda" << std::endl;
    lambda();
}

int main(void)
{
    int foo = 42;
    std::function<void(void)> lambda = [&] { std::cout << foo << std::endl; };
    external_c_function(c_callback_wrapper, &lambda);
    return 0;
}
2
ответ дан Christoph 25 August 2018 в 18:45
поделиться
template<class T>using type=T; // makes some declarations easier

template<class F>
struct callback_t;

template<class F, class Sig>
struct cast_helper_pvoid_last;
template<class F, class R, class... Args>
struct cast_helper_pvoid_last<F, R(Args...)> {
  type<R(*)(Args..., void*)> operator()() const {
    return [](Args... args, void* pvoid)->R {
      auto* callback = static_cast<callback_t<F>*>(pvoid);
      return callback->f( std::forward<Args>(args)... );
    };
  }
};

template<class F>
struct callback_t {
  F f;
  void* pvoid() { return this; }

  template<class Sig>
  auto pvoid_at_end()->decltype( cast_helper_pvoid_last<F, Sig>{}() ) {
    return cast_helper_pvoid_last<F,Sig>{}();
  }
};
template<class T>using decay_t=typename std::decay<T>::type;
template<class F>
callback_t<decay_t<F>> make_callback( F&& f ) { return {std::forward<F>(f)}; }

Пример использования:

int x = 3;
auto callback = make_callback( [&]( int y ) { return x+y; } );
int (*func)(int, void*) = callback.pvoid_at_end<int(int)>();
std::cout << func( 1, callback.pvoid() ) << "\n";

должен печатать 4. (живой пример)

Время жизни вашего callback_t должно превышать pvoid, который он производит.

Я мог бы автоматически выводить подпись указателя функции вместо того, чтобы потребовать вас передать <int(int)> (подпись без void*), но опять же, что делает код намного легче.

и добавьте это, если вы хотите, чтобы void* был первым:

template<class F, class Sig>
struct cast_helper_pvoid_first;
template<class F, class R, class... Args>
struct cast_helper_pvoid_first<class F, R(Args...)> {
  type<R(*)(void*, Args...)> operator()() const {
    return [](void* pvoid, Args... args)->R {
      auto* callback = static_cast<callback<F>*>(pvoid);
      return callback->f( std::forward<Args>(args)... );
    };
  }
};
// inside the callback_t<?> template:
  template<class Sig>
  auto pvoid_at_start()->decltype( cast_helper_pvoid_first<F, Sig>{}() ) {
    return cast_helper_pvoid_first<F,Sig>{}();
  }

, делающий void* в середине, становится более сложным.

Если у вас есть проблемы с вызовами соглашения, нам нужно использовать вспомогательный объект

. Имейте pvoid_at_end, возвратите cast_helper_pvoid_last вместо вызова () на нем.

Затем , добавьте operator перегрузки к указателю на функцию для каждого вызывающего соглашения, которое необходимо поддерживать. Тело идентично operator(), поскольку лямбда должна поддерживать любой из них.

В качестве альтернативы, с некоторой поддержкой C ++ 14 вы можете изменить тип возврата operator() на auto, и оставьте код в противном случае неповрежденным, и полагайтесь на прямой cast-from-lambda, чтобы получить право называть право.

2
ответ дан Yakk - Adam Nevraumont 25 August 2018 в 18:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: