3

I'm facing a situation where I usually would create a custom class by inheriting from boost::hana::tuple. For example, by using the following code,

template<typename ... args>
struct my_nonworking_custom_tuple_t : public boost::hana::tuple<args ...> //not working!!!
{
    using base = boost::hana::tuple<args ...>;
    //... all other stuff

    auto my_getter() const { return base::operator[](1_c); }
};

This, however, doesn't work as boost::hana::tuple is implemented as final.

Thus it seems I'm forced to use composition,

template<typename ... args>
struct my_custom_tuple_t
{
    //... all other stuff
    boost::hana::tuple<args ...> tup;
    auto my_getter() const { return tup[1_c]; }
};

However, as soon as I do that, the resulting class does not model the Hana concept "Sequence" anymore, so that I can't apply all the convenient Hana methods.

What do I need to do to turn my_custom_tuple_t into a Hana Sequence?

davidhigh
  • 14,652
  • 2
  • 44
  • 75

1 Answers1

4

Generally you shouldn't need to do this, but here is a guide for implementing your own Sequence as well as fulfilling requirements for other concepts in Boost.Hana. (The latter of which should be useful to end users who are not providing their own implementation of a tuple.)

Start with the Minimal Complete Definition (MCD) in the documentation for hana::Sequence

There you will see that to implement a Sequence your data type must implement the make function as well as satisfy the requirements for Iterable and Foldable.

So the full list of Hana functions you must provide implementations for are as follows:

  • at
  • drop_front
  • is_empty
  • make
  • unpack

Additionally, note that Boost.Hana has two tuple types tuple and basic_tuple. basic_tuple is more light weight so you should use that for your storage.

To use Boost.Hana's tag dispatching you can implement hana::tag_of or simply provide a hana_tag type alias as a member of your class.

#include <boost/hana.hpp>
#include <utility>

namespace mine {
    struct my_custom_tuple_tag { };

    template<typename ... args>
    struct my_custom_tuple_t {
        using hana_tag = my_custom_tuple_tag;
        //... all other stuff
        boost::hana::basic_tuple<args ...> tup;

        auto my_getter() const {
            return boost::hana::at_c<1>(tup);
        }
    };
}

namespace boost::hana {
    // Iterable

    template <>
    struct at_impl<mine::my_custom_tuple_tag> {
        template <typename Xs, typename N>
        static constexpr decltype(auto) apply(Xs&& xs, N const&) {
            return at_impl<basic_tuple_tag>(std::forward<Xs>(xs).tup, N{});
        }
    };

    template <>
    struct drop_front_impl<mine::my_custom_tuple_tag> {
        template <typename Xs, typename N>
        static constexpr auto apply(Xs&& xs, N const&) {
            return drop_front_impl<basic_tuple_tag>(std::forward<Xs>(xs).tup);
        }
    };

    template <>
    struct is_empty_impl<mine::my_custom_tuple_tag> {
        template <typename Xs>
        static constexpr auto apply(Xs const& xs) {
            return is_empty_impl<basic_tuple_tag>(xs).tup;
        }
    };

    // Foldable

    template <>
    struct unpack_impl<mine::my_custom_tuple_tag> {
        template <typename Xs, typename F>
        static constexpr auto apply(Xs&& xs, F&& f) {
            return unpack_impl<basic_tuple_tag>(std::forward<Xs>(xs).tup,
                                                std::forward<F>(f));
        }
    };

    // Sequence

    template <>
    struct make_impl<mine::my_custom_tuple_tag> {
        template <typename ...Args>
        static constexpr auto apply(Args&& ...args) {
            return make_impl<basic_tuple_tag>(std::forward<Args>(args)...);
        }
    };

    template <>
    struct Sequence<mine::my_custom_tuple_tag> : std::true_type { };
}

It is worth noting that the template for checking Sequence is simply an opt-in template specialization. I'm sure this is just a short cut to save on compile-time computations as the other concepts rely on checking for non-default implementations of their required functions.

https://godbolt.org/z/iaYBFq

Jason Rice
  • 1,686
  • 1
  • 12
  • 17
  • Thanks for the great answer. What is the reason for using a `basic_tuple` instead of `tuple`? – davidhigh May 19 '20 at 09:02
  • `hana::tuple` has the same constructors as `std::tuple` which make it very slow to instantiate. The author created `basic_tuple` to be a light weight equivalent since Hana already has `make`. Even `hana::tuple` itself uses `basic_tuple` as its storage (Ithink). – Jason Rice May 19 '20 at 20:34
  • Yeah it does: https://github.com/boostorg/hana/blob/v1.6.0/include/boost/hana/tuple.hpp#L99. Thanks for the bounty! :D – Jason Rice May 19 '20 at 20:51