【问题标题】:Compile time type detection of parameter types参数类型的编译时类型检测
【发布时间】:2018-01-08 07:58:30
【问题描述】:

我确实知道几种方法来检测给定类是否具有具有指定签名的函数。我想要的是在编译时推断签名。考虑:

struct test_class
{
    void test( int a );
    void test( float b );
};

我可以使用 decltype 和 SFINAE 来检测指定 test() 的存在,其语法很简单,例如 has_test<test_class,int>();。然而,我想要的是test_types<test_class>::types -> mpl::list< int, float > 之类的东西。任何人都有一个理智的想法如何做到这一点?要求是无法准备可检测类型的列表(因此它将检测任何test( T ),而不仅仅是我们“注册”的那些。

【问题讨论】:

  • 不要认为这是可能的。如果班级有template<class T> void test(T)怎么办?
  • 那我不管它——它可能会失败,它可能会堆栈溢出。我只对非模板函数感兴趣。但你是对的,它可能暗示了这项任务的不可能性:P
  • 参数类型 (int,float,...) 是否有任何限制?或者它们可以是字面上的 any 类型?您可以将 test() 签名更改为功能等效的东西吗?
  • 任何类型。如果我有一个 mpl::list,我可以非常简单地做到这一点,但是没有它似乎是不可能的?功能等效是什么意思?不过可能不会。
  • 你不需要类型列表,对允许的类型集有一个总顺序就足够了......所以我问了

标签: c++ templates c++17


【解决方案1】:

如果你负担得起以相当于(我知道这很丑陋,也许你可以做出更漂亮的东西)的方式装饰 test() 重载:

struct test_class
{
    param<int> test( int a, param_id<0> ={} );
    param<float> test( float a, param_id<1> ={} );
};

那么这样的事情应该可以工作(godbolt conformance view):

template<typename T> struct param{ using type = T; };
template<int I> struct param_id{};
template<typename... T> struct type_list{};

struct anything{ template<typename T> operator T&&(); };

template<int I>
struct matcher
{
  template<typename T, typename E = std::enable_if_t<std::is_same<T,param_id<I>>::value> >
  operator T();
};

template<typename T,int I,typename = std::void_t<>,typename... Ts>
struct test_types_impl{ using type = type_list<Ts...>; };

template<typename T,int I,typename... Ts>
struct test_types_impl<T,I,std::void_t<decltype(std::declval<T>().test( anything{}, matcher<I>{} ))>,Ts...>:
  test_types_impl<T,I+1,void,Ts...,typename decltype(std::declval<T>().test( anything{}, matcher<I>{} ))::type>
{
};

template<typename T>
struct test_types{ using type = typename test_types_impl<T,0>::type; };

struct test_class
{
    param<int> test( int a, param_id<0> ={} );
    param<float> test( float a, param_id<1> ={} );
};

static_assert( std::is_same_v<test_types<test_class>::type, type_list<int,float>> );

以上内容至少需要可移动构造的参数类型和 C++17(但我认为它也可以在 C++11 中工作,并且适用于任何类型)。

param_id 如果您设法获得允许的参数类型集的总排序,则可以省略。也许,我们甚至可以以某种方式省略param&lt;T&gt;,但不确定(等待OP对此的反馈:))

【讨论】:

  • 这很有趣,我会玩一下!
  • 我设法将它归结为我想要的简单语法,使用两种类型的“特征”——不幸的是,它们需要排序,其中有两个——我会考虑更多,也许我会想出一些办法 - godbolt.org/g/eC6HX7 - 任何提示都是金的!
  • 现在的问题是 - 我对每种类型都有一个编译时哈希值 - 我如何使用它来代替索引?
  • @KornelKisielewicz 如果类型id 映射是双射的并且所有使用的类型的集合不是太大,您可以从 0 迭代到某个最大值,丢弃不匹配 ...
【解决方案2】:

IIUC,您希望对类应用一些检查以列出类型和累积(?)结果。如果是这样,您可以使用代码,如下所示:

#include <boost/mpl/list.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/empty.hpp>
#include <boost/mpl/front.hpp>
#include <type_traits>

template <typename ClassT, typename ParamT>
struct check : public std::true_type
{
};

// template <typename ClassT>
// struct check<ClassT, double> : public std::false_type
// {
// };

template <typename ClassT, typename ParamList, bool list_empty>
struct apply;

template <typename ClassT, typename ParamList>
struct apply <ClassT, ParamList, true> : public std::true_type{};

template <typename ClassT, typename ParamList>
struct apply <ClassT, ParamList, false> :
public std::integral_constant<
    bool,
    apply<
            ClassT,
            typename boost::mpl::pop_front<ParamList>::type,
            boost::mpl::empty<typename boost::mpl::pop_front<ParamList>::type>::value
        >::value && check<ClassT, typename boost::mpl::front<ParamList>::type>::value>
{
};

class Test
{
};

#include <iostream>
int main(int , char ** ) {

    std::cout << std::boolalpha << apply<Test, boost::mpl::list<int, float, double>, false>::value << std::endl;
    return 0;
}

假设check 是您的自定义检查,它给出了std::true_typestd::false_type。如果您取消注释 double 的专业化,结果将从 true 更改为 false

【讨论】:

  • DLL 中可能引入了不同的测试类型,所以我无法积累任何东西。
  • 从头开始 - 我意识到该列表可能只是用于检测,而不是以后使用。好点子。
猜你喜欢
  • 1970-01-01
  • 2023-04-09
  • 1970-01-01
  • 2020-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-06
相关资源
最近更新 更多