Как написать SFINAE для проверки правила парсера? - PullRequest
3 голосов
/ 26 мая 2011

У меня есть класс sfinae, который проверяет, является ли класс правилом синтаксического анализа (библиотека генератора синтаксического анализа AX). Значение axe::is_rule<P>::value должно быть истинным, если P удовлетворяет требованиям правила синтаксического анализа. Правило синтаксического анализатора должно иметь одну из следующих функций-членов, принимая пару итераторов и возвращая 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), которая, конечно, создает синтаксические бородавки (в конце концов, генератор синтаксического анализа - все о синтаксическом сахаре). *

Как бы вы изменили тест sfinae в is_rule, чтобы охватить приведенные выше неудачные случаи?

1 Ответ

5 голосов
/ 26 мая 2011

Я думаю, что API is_rule недостаточно.Например, unfortunate - это правило, только если оно используется с итераторами типа const unsigned char*.Если вы используете unfortunate с const 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.Это может быть очень удобно для отправки тегов.Например:

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*>());}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...