How to write SFINAE to check the parser rule?

I have an sfinae class that checks if a class is a parser rule (AX parser generator library). axe::is_rule<P>::valuemust evaluate to true if P satisfies the requirements of the parser rule. The parser rule must have one of the following member functions, taking a pair of iterators and returning axe::result<Iterator>:

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

or his specialization, or not a template for some type of CharT

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

or const version above. Theoretically, there may be more than one overloaded operator(), although in practice one single operator()with one of the above signatures is enough .

Unfortunately, the current implementation is_ruleprovides only some, but not all, cases. There are some unsuccessful classes that fail the test 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");

For example, the following unsuccessful types fail the test:

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);

The current solution in AX is to wrap them in a transport package ( class r_ref_t), which, of course, creates parsing warts (after all, the parser is all about parsing sugar).

How would you modify sfinae's test is_ruleto cover the unfortunate cases above?

+3
source share
1 answer

, API is_rule . , unfortunate const unsigned char*. unfortunate const char*, , , ?

API :

template <class R, class It> struct is_rule;

, ++ 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>, sub std::is_same, std::is_convertible.

is_rule std::integral_constant. . :.

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*>());}
+5

All Articles