boostorg / spirit

Boost.org spirit module
http://boost.org/libs/spirit
392 stars 161 forks source link

X3: MPL usage #346

Open Kojoley opened 6 years ago

Kojoley commented 6 years ago

Currently X3 uses MPL which is still not updated for C++11. It is slow, even including it adds a big time overhead (see benchmarks on hana docs), but actually it is used intensively only in a few places. What if we rewrite them without MPL?

Example: https://github.com/boostorg/spirit/blob/49587ff7a04d0301d798d080e541f45debbb737f/include/boost/spirit/home/x3/operator/detail/sequence.hpp#L237-L294

This could be easily done without any meta-library (and twice shorter):

// a helper class, may be reused in multiple places
template <typename... T>
struct type_pack
{
    template <typename... U>
    using prepend = type_pack<U..., T...>;
};

template <typename T, typename Attribute>
struct attribute_prepender { using type = typename T::template prepend<Attribute>; };
// filter unused attribute
template <typename T>
struct attribute_prepender<T, unused_type> { using type = T; };

template <typename T, typename P, typename C>
using prepend_parser_attribute = typename attribute_prepender<T, traits::attribute_of_t<P, C>>::type;

template <typename C, typename L, typename... T>
struct get_sequence_types;
// place underlying parsers of sequence_parser to the queue
template <typename C, typename T, typename L, typename R, typename... U>
struct get_sequence_types<C, T, sequence_parser<L, R>, U...> : get_sequence_types<C, T, R, L, U...> {};
// prepend an attribute of parser to type pack
template <typename C, typename T, typename H, typename... U>
struct get_sequence_types<C, T, H, U...> : get_sequence_types<C, prepend_parser_attribute<T, H, C>, U...> {};
// we are done, wrap into fusion::deque
template <typename C, typename... T>
struct get_sequence_types<C, type_pack<T...>> { using type = fusion::deque<T...>; };
// do not wrap if only a single attribute
template <typename C, typename T>
struct get_sequence_types<C, type_pack<T>> { using type = T; };
// return unused_type if no attributes in the pack
template <typename C>
struct get_sequence_types<C, type_pack<>> { using type = unused_type; };

template <typename L, typename R, typename C>
struct attribute_of_sequence : get_sequence_types<C, type_pack<>, R, L> {};

http://coliru.stacked-crooked.com/a/404f73f1f2340091

Proof of concept code ```cpp #include #include #include #include #include template struct type_pack { template using prepend = type_pack; }; struct unused_type {}; struct context {}; template struct parser {}; template struct attr_parser : parser> { using attribute_type = T; }; namespace traits { template struct attribute_of { using type = typename R::attribute_type; }; template using attribute_of_t = typename R::attribute_type; } template struct sequence_parser : parser> {}; template struct attribute_prepender { using type = typename T::template prepend; }; // filter unused attribute template struct attribute_prepender { using type = T; }; template using prepend_parser_attribute = typename attribute_prepender>::type; template struct get_sequence_types; // place underlying parsers of sequence_parser to the queue template struct get_sequence_types, U...> : get_sequence_types {}; // prepend an attribute of parser to type pack template struct get_sequence_types : get_sequence_types, U...> {}; // we are done, wrap into fusion::deque template struct get_sequence_types> { using type = std::tuple; }; // do not wrap if only a single attribute template struct get_sequence_types> { using type = T; }; // return unused_type if no attributes in the pack template struct get_sequence_types> { using type = unused_type; }; template struct attribute_of_sequence : get_sequence_types, R, L> {}; int main() { using seq_left = sequence_parser< sequence_parser, attr_parser> , attr_parser >; using seq_right = sequence_parser< attr_parser , sequence_parser, attr_parser> >; using attr_tup = typename attribute_of_sequence::type; static_assert(std::is_same>::value, ":("); std::cout << "attr_tup: " << boost::core::demangle(typeid(attr_tup).name()) << std::endl; using attr_plain = typename attribute_of_sequence, attr_parser, context>::type; static_assert(std::is_same::value, ":("); std::cout << "attr_plain: " << boost::core::demangle(typeid(attr_plain).name()) << std::endl; using attr_unused = typename attribute_of_sequence, attr_parser, context>::type; static_assert(std::is_same::value, ":("); std::cout << "attr_unused: " << boost::core::demangle(typeid(attr_unused).name()) << std::endl; return 0; } ```

Checked on GCC 4.7.3, Clang 3.2, MSVC 14.0

Kojoley commented 6 years ago

Updated the code for a single and zero attribute sequences.

djowel commented 6 years ago

Go for it! 👍 I've been wanting to do that for X3 for a long time now.

Kojoley commented 6 years ago

Well, I have discovered that Fusion heavily relies on MPL, includes a lot of support headers, and some of fusion::traits is not ::value friendly (like is_associative).

Also, some of Spirit traits (like is_substitute or variant_find_substitute) are not properly tested. This makes the work on replacing them error prone.

djowel commented 6 years ago

Well, I have discovered that Fusion heavily relies on MPL

True. And it will take a lot of effort to decouple MPL from Fusion, not that it can't be done... But I am not sure it's worth the effort.

Kojoley commented 6 years ago

Sigh, boost::variant also uses MPL a lot. It is a surprise why MPL is not updated for C++11 if so many boost libraries uses it.

Mike-Devel commented 5 years ago

Have you considered moving to mp11 (for the cases where std c++14 facilities are not enough)?

Kojoley commented 5 years ago

There is no need in meta-programming libraries for X3, but MPL is required to access MPL interfaces of boost::variant and Fusion sequences.