Reputation: 319
I want:
template<class ... T>
auto foo()
{
// ✨ magic ✨
}
Such that:
(foo<int, char, bool>() == foo<char, int, bool>()) // True
(foo<int>() == foo<char>()) // False
In other words, I want foo to return a unique id for the combination of types passed to it rather than the permutation of types passed to it.
My first idea was that there might be some way to sort the parameter pack at compile time, though I'm not sure how exactly that would work.
My current solution is this:
// Precondition: Client must pass the parameters in alphabetical order to ensure the same result each time
template<class ... T>
std::type_index foo()
{
return std::make_type_index(typeid(std::tuple<T ... >));
}
The problem with this is that it doesn't work if the client is using type aliases. For example:
using my_type = char;
(foo<bool, int, my_type>() == foo<bool, char, int>()) // False
One idea I had is to assign a new prime number as an id for every new type that the function handles. Then I could assign a unique id for a particular combination of types by multiplying their prime number ids together. For example:
id of int = 2
id of bool = 3
id of char = 5
id of <int, bool, char> = 2 * 3 * 5 = 30
id of <bool, int, char> = 3 * 2 * 5 = 30
Only problem is how I'm going to assign unique prime number ids to each type without incurring a runtime penalty.
Upvotes: 1
Views: 449
Reputation: 42916
You can use the gcc /clang's extension __PRETTY_FUNCTION__
(__FUNCSIG__
in msvc) as the identifier of each type and save them into std::array
, then sort it to get the unique combination of type list.
But since std::arrays
of different sizes cannot be compared with each other, we also need create a new type (unique_id
) to wrap the sorted array and overload its operator==
:
#include <algorithm>
#include <array>
#include <string_view>
template <class T>
constexpr std::string_view id() {
#ifdef __GNUC__
return __PRETTY_FUNCTION__;
#elif defined(_MSC_VER)
return __FUNCSIG__;
#endif
}
template<std::size_t N>
class unique_id {
std::array<std::string_view, N> value;
public:
constexpr unique_id(const std::array<std::string_view, N>& value) : value(value) { }
template<std::size_t M>
constexpr bool operator==(const unique_id<M>& other) const {
if constexpr (N == M) return value == other.value;
else return false;
}
};
template<class... Ts>
constexpr auto foo() {
std::array<std::string_view, sizeof...(Ts)> names{id<Ts>()...};
std::ranges::sort(names);
return unique_id(names);
}
static_assert(foo<int, char, bool>() == foo<char, int, bool>());
static_assert(foo<int>() != foo<char>());
Upvotes: 2
Reputation: 303397
With Boost.Mp11:
template <typename T>
constexpr std::string_view type_name() {
return __PRETTY_FUNCTION__; // close enough
}
template <typename T, typename U>
using type_less = mp_bool<type_name<T>() < type_name<U>()>;
template <typename... Ts>
constexpr auto foo() {
return type_name<mp_sort<mp_list<Ts...>, type_less>>();
}
static_assert(foo<int, char>() == foo<char, int>());
Upvotes: 2
Reputation: 13484
Using Boost.Hana:
#include <boost/hana/set.hpp>
#include <boost/hana/type.hpp>
template<typename... T>
constexpr auto foo() {
return boost::hana::make_set(boost::hana::type_c<T>...);
}
using my_type = char;
static_assert(foo<bool, int, my_type>() == foo<bool, char, int>());
Upvotes: 5