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?
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:
atdrop_frontis_emptymakeunpackAdditionally, 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With