C++ 如何获取可调用类型的签名?

C++ 如何获取可调用类型的签名?,c++,templates,stl,c++17,template-meta-programming,C++,Templates,Stl,C++17,Template Meta Programming,我想构建一个漂亮、现代的界面来构建计算树,类似这样: auto [F, G] = calcs.emplace( [](int a, int b){ return a + b; }, [](){ return 4; } ); 我从中得到了灵感,但这里我们可能也会添加参数和返回类型,问题来了:我们如何推断给定可调用对象的底层存储类型,以及如何将它们存储在集合中?用当前的语言特性创建这样一个简单的api有可能吗 我在谷歌上搜索了几个小时,似乎返回类型是一个较小

我想构建一个漂亮、现代的界面来构建计算树,类似这样:

auto [F, G] = calcs.emplace(
        [](int a, int b){ return a + b; },
        [](){ return 4; }
    );
我从中得到了灵感,但这里我们可能也会添加参数和返回类型,问题来了:我们如何推断给定可调用对象的底层存储类型,以及如何将它们存储在集合中?用当前的语言特性创建这样一个简单的api有可能吗


我在谷歌上搜索了几个小时,似乎返回类型是一个较小的问题,但我不知道参数。

Q:如何获得签名

A:通过调用的
operator()
方法上的模式匹配,例如:

template <class TMethodType>
struct ReadSignature;

// Const specialization
template <class TReturnType, class TClass, class ... TArgsTypes>
struct ReadSignature<TReturnType(TClass::*)(TArgsTypes...) const> {
    using ReturnType = TReturnType;
    using Class = TClass;
    using Args = std::tuple<TArgsTypes...>;

    static constexpr bool is_const = true;
};

// Non-const specialization
// This is for mutable lambdas, e.g. []() mutable {}
template <class TReturnType, class TClass, class ... TArgsTypes>
struct ReadSignature<TReturnType(TClass::*)(TArgsTypes...)> {
    using ReturnType = TReturnType;
    using Class = TClass;
    using Args = std::tuple<TArgsTypes...>;

    static constexpr bool is_const = false;
};

模板
结构读取签名;
//常数特化
模板
结构读取签名{
使用ReturnType=TReturnType;
使用Class=TClass;
使用Args=std::tuple;
静态constexpr bool是_const=true;
};
//非常量专门化
//这是针对可变lambda的,例如。[]()可变{}
模板
结构读取签名{
使用ReturnType=TReturnType;
使用Class=TClass;
使用Args=std::tuple;
静态constexpr bool是_const=false;
};
您可以这样使用它:

    auto callable = [](int x, int y) { return x + y; };
    using Class = decltype(callable);
    using Signature = ReadSignature<decltype(&Class::operator())>;
class CallableI {
    virtual ~CallableI() = default;

    virtual std::any call(const std::vector<std::any>& args) = 0;

    // For type checking:
    virtual size_t arg_count() = 0;
    virtual std::type_info get_arg_type_info(size_t arg_index) = 0;
    virtual std::type_info get_return_type_info() = 0;
};
autocallable=[](intx,inty){返回x+y;};
使用Class=decltype(可调用);
使用签名=重新签名;

Q:如何在集合中存储可调用项

A:您需要以某种方式删除类型。对于可调用对象,使包装器接口看起来很自然。例如,类似这样的内容:

    auto callable = [](int x, int y) { return x + y; };
    using Class = decltype(callable);
    using Signature = ReadSignature<decltype(&Class::operator())>;
class CallableI {
    virtual ~CallableI() = default;

    virtual std::any call(const std::vector<std::any>& args) = 0;

    // For type checking:
    virtual size_t arg_count() = 0;
    virtual std::type_info get_arg_type_info(size_t arg_index) = 0;
    virtual std::type_info get_return_type_info() = 0;
};
class CallableI{
virtual~CallableI()=默认值;
虚拟std::any调用(const std::vector&args)=0;
//对于类型检查:
虚拟大小参数计数()=0;
虚拟标准::类型参数信息获取类型参数信息(大小参数索引)=0;
虚拟std::type_info get_return_type_info()=0;
};

然后编写一个实现该接口的模板类,该接口将为每个lambda参数实例化。在
calcs
对象中,您实际存储了
std::vector

Q:如何获取签名

A:通过调用的
operator()
方法上的模式匹配,例如:

template <class TMethodType>
struct ReadSignature;

// Const specialization
template <class TReturnType, class TClass, class ... TArgsTypes>
struct ReadSignature<TReturnType(TClass::*)(TArgsTypes...) const> {
    using ReturnType = TReturnType;
    using Class = TClass;
    using Args = std::tuple<TArgsTypes...>;

    static constexpr bool is_const = true;
};

// Non-const specialization
// This is for mutable lambdas, e.g. []() mutable {}
template <class TReturnType, class TClass, class ... TArgsTypes>
struct ReadSignature<TReturnType(TClass::*)(TArgsTypes...)> {
    using ReturnType = TReturnType;
    using Class = TClass;
    using Args = std::tuple<TArgsTypes...>;

    static constexpr bool is_const = false;
};

模板
结构读取签名;
//常数特化
模板
结构读取签名{
使用ReturnType=TReturnType;
使用Class=TClass;
使用Args=std::tuple;
静态constexpr bool是_const=true;
};
//非常量专门化
//这是针对可变lambda的,例如。[]()可变{}
模板
结构读取签名{
使用ReturnType=TReturnType;
使用Class=TClass;
使用Args=std::tuple;
静态constexpr bool是_const=false;
};
您可以这样使用它:

    auto callable = [](int x, int y) { return x + y; };
    using Class = decltype(callable);
    using Signature = ReadSignature<decltype(&Class::operator())>;
class CallableI {
    virtual ~CallableI() = default;

    virtual std::any call(const std::vector<std::any>& args) = 0;

    // For type checking:
    virtual size_t arg_count() = 0;
    virtual std::type_info get_arg_type_info(size_t arg_index) = 0;
    virtual std::type_info get_return_type_info() = 0;
};
autocallable=[](intx,inty){返回x+y;};
使用Class=decltype(可调用);
使用签名=重新签名;

Q:如何在集合中存储可调用项

A:您需要以某种方式删除类型。对于可调用对象,使包装器接口看起来很自然。例如,类似这样的内容:

    auto callable = [](int x, int y) { return x + y; };
    using Class = decltype(callable);
    using Signature = ReadSignature<decltype(&Class::operator())>;
class CallableI {
    virtual ~CallableI() = default;

    virtual std::any call(const std::vector<std::any>& args) = 0;

    // For type checking:
    virtual size_t arg_count() = 0;
    virtual std::type_info get_arg_type_info(size_t arg_index) = 0;
    virtual std::type_info get_return_type_info() = 0;
};
class CallableI{
virtual~CallableI()=默认值;
虚拟std::any调用(const std::vector&args)=0;
//对于类型检查:
虚拟大小参数计数()=0;
虚拟标准::类型参数信息获取类型参数信息(大小参数索引)=0;
虚拟std::type_info get_return_type_info()=0;
};

然后编写一个实现该接口的模板类,该接口将为每个lambda参数实例化。在你的
calcs
对象中,你实际上存储了
std::vector

你不会得到一种类型,每个签名有一种类型,你可以将它们存储在
std::function
中。你需要详细说明你想要它实际做什么,以及你期望它如何工作。您想如何处理具有不同数量参数的可调用项?@MatthieuBrucher,但您不能在同一集合中存储不同的
std::function
模板实例化(没有其他级别的类型擦除)。“method_traits”使用
&C::operator()的签名,但是只需要一个非模板的
操作符()
…你能展示更完整的用法吗?你不会得到一种类型,每个签名有一种类型,你可以将它们存储在
std::function
中。你需要详细说明你想要它实际做什么,以及你期望它如何工作。您想如何处理具有不同数量参数的可调用项?@MatthieuBrucher,但您不能在同一集合中存储不同的
std::function
模板实例化(没有其他级别的类型擦除)。“method_traits”使用
&C::operator()的签名,但是只需要1个非模板的
操作符()
…您能显示更完整的用法吗?