I have a sfinae class that tests whether a class is a parser rule (AXE parser generator library).
The axe::is_rule<P>::value should evaluate to true iff P satisfies parser rule requirements. A 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 its specialization, or non-template for some type CharT
axe::result<CharT*> P::operator()(CharT*, CharT*);
, or const versions of the above. Theoretically, there can be more than one overloaded operator(), though in practice a test for a single operator() with one of the above signatures would suffice.
Unfortunately, current implementation of is_rule takes care of only some, but not all cases. There are some unfortunate classes, that fail the is_rule test:
#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 unfortunate 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);
Current solution in AXE is to wrap those in a forwarding wrapper (class r_ref_t), which, of course, creates syntactic warts (after all, parser generator is all about syntactic sugar).
How would you modify the sfinae test in is_rule to cover the unfortunate cases above?
I think the API of
is_ruleis not sufficient. For exampleunfortunateis a rule only if used with iterators of typeconst unsigned char*. If you useunfortunatewithconst char*, then it doesn’t work, and is thus not a rule, right?That being said, if you change the API to:
then I think this is doable in C++11. Below is a prototype:
For me this prints out:
I made the rule slightly more lax than you specified: The return type only has to be implicitly convertible to
axe::result<It>. If you really want it to be exactlyaxe::result<It>then just sub instd::is_samewhere I usedstd::is_convertible.I also made
is_rulederive fromstd::integral_constant. This can be very convenient for tag dispatching. E.g.: