Search code examples
c++boostboost-hana

Checking for a specific nested type/tag with boost hana


Having fun with boost::hana. I wish to check for a specific nested type that acts like a tag in another type, so I borrow from hana::when_valid example and defined a class is_S along with its SFINAE-enabled specialization:

#include <iostream>

#include <boost/hana/core/when.hpp>
namespace hana = boost::hana;

#define V(x) std::cout << x << std::endl

struct S_tag { };

struct S {
    using tag = S_tag;
};

struct T {
    using tag = int;
};

template< typename T, typename = hana::when< true > >
struct is_S {
    static constexpr bool value = false;
};

template< typename T >
struct is_S< T, hana::when_valid< typename T::tag > > {
    static constexpr bool value = std::is_same<
        typename T::tag, S_tag >::value;
};

int main () {
    std::cout << "is_S (    S { }) = "; V ((is_S< S >::value));
    std::cout << "is_S (    T { }) = "; V ((is_S< T >::value));
    std::cout << "is_S (float { }) = "; V ((is_S< float >::value));

    return 0;
}

This prints:

$ clang++ -std=c++1z sfinae.cpp && ./a.out | c++filt
is_S (    S { }) = 1
is_S (    T { }) = 0
is_S (float { }) = 0

Is there a simpler/shorter/more succinct way of writing the same check, in keeping with value-type computation of hana philosophy?


Solution

  • Here's what I might write:

    #include <boost/hana.hpp>
    #include <iostream>
    namespace hana = boost::hana;
    
    
    struct S_tag { };
    struct S { using tag = S_tag; };
    struct T { using tag = int; };
    
    auto tag_of = [](auto t) -> hana::type<typename decltype(t)::type::tag> {
        return {};
    };
    
    auto is_S = [](auto t) {
        return hana::sfinae(tag_of)(t) == hana::just(hana::type<S_tag>{});
    };
    
    int main() {
        std::cout << "is_S (    S { }) = " << is_S(hana::type<S>{})() << std::endl;
        std::cout << "is_S (    T { }) = " << is_S(hana::type<T>{})() << std::endl;
        std::cout << "is_S (float { }) = " << is_S(hana::type<float>{})() << std::endl;
    }