Информация об изменениях

Сообщение Re: Получить сигнатуру из ламбды от 14.08.2020 7:15

Изменено 14.08.2020 7:24 rg45

Re: Получить сигнатуру из ламбды
Здравствуйте, Barbar1an, Вы писали:

B>если у нас

B>
B>template<class L> Subscribe(L lambda)
B>{
B>    std::function<сигнатура вызова lambdы> f; // типа void(int, int)
B>    std::function<void(сигнатура параметров lambdы)> f; // типа только "int, int"
B>}

B>Subscribe([](int, int){});
B>

B>возможно както?

Да в принципе, не сложно, если вспомнить, что все лямбды — это просто объекты классов с перегруженными operator(). Можно даже без концептов и SFINAE. И можно даже предоставить универсальную реализацию, которая будет применима не только к лямбдам, но и к обычным функциям и к определенным пользователем классам функциональных объектов.

Ниже эскизная реализация. До полной реализации здесь не хватает поддержки 'const', 'volatile', '&', '&&', 'nothrow' и всех их комбинаций.

http://coliru.stacked-crooked.com/a/7c29edffdf60891a

#include <string>
#include <tuple>
#include <type_traits>

template <typename>
struct CallableTraits;

template <typename ReturnT, typename...ParamT>
struct CallableTraits<ReturnT(ParamT...)>
{
    static constexpr size_t Arity = sizeof...(ParamT);
    using Signature = ReturnT(ParamT...);
    using ReturnType = ReturnT;
    using ParameterTypes = std::tuple<ParamT...>;
};

template <typename ReturnT, typename...ParamT>
struct CallableTraits<ReturnT(*)(ParamT...)> : CallableTraits<ReturnT(ParamT...)> {};

template <typename T, typename ReturnT, typename...ParamT>
struct CallableTraits<ReturnT(T::*)(ParamT...)> : CallableTraits<ReturnT(ParamT...)> {};

template <typename T, typename ReturnT, typename...ParamT>
struct CallableTraits<ReturnT(T::*)(ParamT...) const> : CallableTraits<ReturnT(ParamT...)> {};

template <typename T>
struct CallableTraits : CallableTraits<decltype(&std::decay_t<T>::operator())> {};

template <typename T>
constexpr size_t CallableArity = CallableTraits<T>::Arity;

template <typename T>
using CallableSignature = typename CallableTraits<T>::Signature;

template <typename T>
using CallableReturnType = typename CallableTraits<T>::ReturnType;

template <typename T, size_t I>
using CallableParameterType = std::tuple_element_t<I, typename CallableTraits<T>::ParameterTypes>;

int main()
{
    int i = 42;

    auto lambda = [=](int, double, const std::string&) { return i; };

    using L = decltype(lambda);

    static_assert(CallableArity<L> == 3, "");
    static_assert(std::is_same_v<CallableSignature<L>, int(int, double, const std::string&)>, "");
    static_assert(std::is_same_v<CallableParameterType<L, 0>, int>, "");
    static_assert(std::is_same_v<CallableParameterType<L, 1>, double>, "");
    static_assert(std::is_same_v<CallableParameterType<L, 2>, const std::string&>, "");
    static_assert(std::is_same_v<CallableReturnType<L>, int>, "");
}
Re: Получить сигнатуру из ламбды
Здравствуйте, Barbar1an, Вы писали:

B>если у нас

B>
B>template<class L> Subscribe(L lambda)
B>{
B>    std::function<сигнатура вызова lambdы> f; // типа void(int, int)
B>    std::function<void(сигнатура параметров lambdы)> f; // типа только "int, int"
B>}

B>Subscribe([](int, int){});
B>

B>возможно както?

Да в принципе, не сложно, если вспомнить, что все лямбды — это просто объекты классов с перегруженными operator(). Можно даже без концептов и SFINAE. И можно даже предоставить универсальную реализацию, которая будет применима не только к лямбдам, но и к обычным функциям и к определенным пользователем классам функциональных объектов.

Ниже эскизная реализация. До полной реализации здесь не хватает поддержки 'const', 'volatile', '&', '&&', 'nothrow' и всех их комбинаций (для поддержки пользовательских классов функциональных объектов).

http://coliru.stacked-crooked.com/a/7c29edffdf60891a

#include <string>
#include <tuple>
#include <type_traits>

template <typename>
struct CallableTraits;

template <typename ReturnT, typename...ParamT>
struct CallableTraits<ReturnT(ParamT...)>
{
    static constexpr size_t Arity = sizeof...(ParamT);
    using Signature = ReturnT(ParamT...);
    using ReturnType = ReturnT;
    using ParameterTypes = std::tuple<ParamT...>;
};

template <typename ReturnT, typename...ParamT>
struct CallableTraits<ReturnT(*)(ParamT...)> : CallableTraits<ReturnT(ParamT...)> {};

template <typename T, typename ReturnT, typename...ParamT>
struct CallableTraits<ReturnT(T::*)(ParamT...)> : CallableTraits<ReturnT(ParamT...)> {};

template <typename T, typename ReturnT, typename...ParamT>
struct CallableTraits<ReturnT(T::*)(ParamT...) const> : CallableTraits<ReturnT(ParamT...)> {};

template <typename T>
struct CallableTraits : CallableTraits<decltype(&std::decay_t<T>::operator())> {};

template <typename T>
constexpr size_t CallableArity = CallableTraits<T>::Arity;

template <typename T>
using CallableSignature = typename CallableTraits<T>::Signature;

template <typename T>
using CallableReturnType = typename CallableTraits<T>::ReturnType;

template <typename T, size_t I>
using CallableParameterType = std::tuple_element_t<I, typename CallableTraits<T>::ParameterTypes>;

int main()
{
    int i = 42;

    auto lambda = [=](int, double, const std::string&) { return i; };

    using L = decltype(lambda);

    static_assert(CallableArity<L> == 3, "");
    static_assert(std::is_same_v<CallableSignature<L>, int(int, double, const std::string&)>, "");
    static_assert(std::is_same_v<CallableParameterType<L, 0>, int>, "");
    static_assert(std::is_same_v<CallableParameterType<L, 1>, double>, "");
    static_assert(std::is_same_v<CallableParameterType<L, 2>, const std::string&>, "");
    static_assert(std::is_same_v<CallableReturnType<L>, int>, "");
}