Автоматически выводить лямбда-аргументы из сигнатуры функции-члена - PullRequest
2 голосов
/ 07 ноября 2019

Обычно я использую лямбда-функции для установки обратных вызовов к функциям-членам для сторонних библиотек. Например:

setCallback([this](auto&& ...x) { handleCallback(std::forward<decltype(x)>(x)...); });

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

#include <functional>
#include <iostream>

class Library
{
public:
    typedef std::function<void (int)> IntFunction;
    typedef std::function<void (int, int)> IntIntFunction;

    void setCallback(IntFunction f) { f_int_ = f; }
    void setCallback(IntIntFunction f) { f_int_int_ = f; }

    void callCallback(int a, int b) {
        if (f_int_) f_int_(a);
        if (f_int_int_) f_int_int_(a, b);
    }

private:
    IntFunction f_int_;
    IntIntFunction f_int_int_;
};

class MyClass
{
public:
    MyClass() {
        //lib.setCallback([this](auto&& ...x) { handleCallback(std::forward<decltype(x)>(x)...); });
        lib.setCallback([this](int a, int b) { handleCallback(a, b); });
    }

    void handleCallback(int a, int b) {
        std::cout << "handleCallback: " << a << ", " << b << std::endl;
    }

    Library lib;
};

int main()
{
    MyClass myclass;
    myclass.lib.callCallback(2, 3);
    return 0;
}

Есть ли способ автоматического вывода правильных аргументовиз функции handleCallback, чтобы избежать дублирования аргументов функции в лямбде?

Ответы [ 3 ]

3 голосов
/ 07 ноября 2019

Вы можете создать для этого функцию:

class MyClass
{
    template <typename Ret, typename ...Args>
    void setCallback(Ret (MyClass::*member)(Args...) /*const*/) {
        lib.setCallback([=](Args...args)
            {
                (this->*member)(std::forward<decltype(args)>(args)...);
            });
    }

public:
    MyClass() { setCallback(&MyClass::handleCallback); }

    void handleCallback(int a, int b) {
        std::cout << "handleCallback: " << a << ", " << b << std::endl;
    }

    Library lib;
};

Демо

2 голосов
/ 07 ноября 2019

Хитрость заключается в том, чтобы должным образом поднять функцию, особенно выражение возвращаемого типа, так как это включит SFINAE, позволяя конструктору std::function определить, может ли быть вызвана лямбда-выражение:

lib.setCallback([this](auto&& ...x) -> 
    decltype(void(handleCallback(std::forward<decltype(x)>(x)...))) {
        handleCallback(std::forward<decltype(x)>(x)...);
    }
);

Живой пример

Вы также можете переслать noexceptness:

lib.setCallback([this](auto&& ...x)
    noexcept(noexcept(handleCallback(std::forward<decltype(x)>(x)...)))
    -> decltype(void(handleCallback(std::forward<decltype(x)>(x)...))) {
        handleCallback(std::forward<decltype(x)>(x)...);
    }
);

В этот момент единственный разумный способ сделать это - использовать макрос:

#define RETURNS(...) noexcept(noexcept(__VA_ARGS__)) \
    -> decltype((__VA_ARGS__)) {                         \
        return __VA_ARGS__;                              \
    }

И используйте это так:

lib.setCallback([this](auto&& ...x) RETURNS(handleCallback(std::forward<decltype(x)>(x)...)));

Живой пример

0 голосов
/ 08 ноября 2019

Из приведенных ответов я построил эту функцию:

#include <type_traits>
#include <utility>

template <typename TClass, typename TRet, typename ...Args>
auto getLambda(TClass *instance, TRet (TClass::*member)(Args...))
{
    if constexpr (std::is_void<TRet>::value)
        return [=](Args...args){ (instance->*member)(std::forward<decltype(args)>(args)...); };
    else
        return [=](Args...args){ return (instance->*member)(std::forward<decltype(args)>(args)...); };
}

Она может быть использована следующим образом:

lib.setCallback(getLambda(this, &MyClass::handleCallback));

Демо

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...