如何编写SFINAE来测试解析器规则?

时间:2011-05-25 21:03:53

标签: c++ c++11

我有一个sfinae类,用于测试类是否是解析器规则(AX解析器生成器库)。 如果P满足解析器规则要求,则axe::is_rule<P>::value应评估为true。解析器规则必须具有以下成员函数之一,使用一对迭代器并返回axe::result<Iterator>:

template<class Iterator>
axe::result<Iterator> P::operator()(Iterator, Iterator);

,或其特化,或某些类型CharT的非模板

axe::result<CharT*> P::operator()(CharT*, CharT*);

,或上面的const版本。从理论上讲,可能会有多个重载operator(),但实际上,使用上述签名之一对单个operator()进行测试就足够了。

不幸的是,is_rule的当前实现仅处理一些但不是所有情况。有一些不幸的类,没有通过is_rule测试:

#define AXE_ASSERT_RULE(T)\
    static_assert(axe::is_rule<typename std::remove_reference<T>::type>::value, \
    "type '" #T "' is not a rule");

例如,以下不幸的类型未通过测试:

struct unfortunate 
{ 
   axe::result<const unsigned char*> 
   operator()(const unsigned char*, const unsigned char*); 
};

AXE_ASSERT_RULE(unfortunate);

// or same using lambda
auto unfortunate1 = [](const unsigned char*, const unsigned char*)
->axe::result<const unsigned char*> {};
AXE_ASSERT_RULE(decltype(unfortunate1));


typedef std::vector<char>::iterator vc_it;
struct unfortunate2 { axe::result<vc_it> operator()(vc_it, vc_it) const; };
AXE_ASSERT_RULE(unfortunate2);

typedef axe::result<const char*> (unfortunate3)(const char*, const char*);
AXE_ASSERT_RULE(unfortunate3);

struct rule { template<class I> axe::result<I> operator()(I, I); };
class unfortunate4 : public rule {};
AXE_ASSERT_RULE(unfortunate4);

AX中的当前解决方案是将它们包装在转发包装器(class r_ref_t)中,这当然会产生语法瑕疵(毕竟,解析器生成器都是关于语法糖的。)

您如何修改is_rule中的sfinae测试以涵盖上述不幸的案例?

1 个答案:

答案 0 :(得分:5)

我认为is_rule的API还不够。例如,unfortunate仅在与const unsigned char*类型的迭代器一起使用时才是规则。如果您将unfortunateconst char*一起使用,那么它不起作用,因此不是规则,对吗?

话虽如此,如果您将API更改为:

template <class R, class It> struct is_rule;

然后我认为这在C ++ 11中是可行的。以下是原型:

#include <type_traits>

namespace axe
{

template <class It>
struct result
{
};

}

namespace detail
{

struct nat
{
    nat() = delete;
    nat(const nat&) = delete;
    nat& operator=(const nat&) = delete;
    ~nat() = delete;
};

struct any
{
    any(...);

    nat operator()(any, any) const;
};

template <class T>
struct wrap
    : public any,
      public T
{
};

template <bool, class R, class It>
struct is_rule
{
     typedef typename std::conditional<std::is_const<R>::value,
                                      const wrap<R>,
                                      wrap<R>>::type W;

   typedef decltype(
                std::declval<W>()(std::declval<It>(), std::declval<It>())
                    ) type;

    static const bool value = std::is_convertible<type, axe::result<It>>::value;
};

template <class R, class It>
struct is_rule<false, R, It>
{
    static const bool value = false;
};

}  // detail

template <class R, class It>
struct is_rule
    : public std::integral_constant<bool,
                         detail::is_rule<std::is_class<R>::value, R, It>::value>
{
};

struct unfortunate 
{ 
   axe::result<const unsigned char*> 
   operator()(const unsigned char*, const unsigned char*); 
};

#include <iostream>

int main()
{
    std::cout << is_rule<unfortunate, const unsigned char*>::value << '\n';
    std::cout << is_rule<unfortunate, const char*>::value << '\n';
}

对我来说,打印出来:

1
0

我使规则比你指定的略宽一些:返回类型只能隐式转换为axe::result<It>。如果您确实希望它完全axe::result<It>,那么只需将std::is_same放在我使用std::is_convertible的位置。

我还is_rule来自std::integral_constant。这对标签调度来说非常方便。 E.g:

template <class T>
void imp(T, std::false_type);

template <class T>
void imp(T, std::true_type);

template <class T>
void foo(T t) {imp(t, is_rule<T, const char*>());}