Info
-
Did you know about
typename erasure
technique (via Strong/Opaque Typedefs) in C++?
Example
template <class...> struct foo {};
struct strong_typedef : foo<int, double> {};
template <fixed_string Str> struct named_strong_typedef;
template<fixed_string Str, class T> [[nodiscard]] constexpr auto typename_cast(const T& t);
int main() {
strong_typedef s1{}; // strong typededf
foo<int, double> f{};
auto s2 = typename_cast<"foo">(f); // named strong typedef
}
Puzzle
- Can you implement a generic version of typename cast?
template <auto Str>
struct te; // TODO
template <auto N, class T>
[[nodiscard]] constexpr auto typename_cast(T&&); // TODO
template <class...>
struct foo {};
template <class... Ts>
constexpr auto fn() {
foo<Ts...> f{};
return typename_cast</*TODO*/ 0>(f);
}
template <class>
constexpr auto is_te_v = false;
template <auto N>
constexpr auto is_te_v<te<N>> = true;
static_assert(std::is_base_of_v<foo<>, decltype(fn())>);
static_assert(is_te_v<decltype(fn())>);
static_assert(not std::is_base_of_v<foo<>, decltype(fn<int>())>);
static_assert(std::is_base_of_v<foo<int>, decltype(fn<int>())>);
static_assert(is_te_v<decltype(fn<int>())>);
static_assert(not std::is_base_of_v<foo<double>, decltype(fn<int>())>);
static_assert(typeid(fn<int>()) == typeid(fn<int>()));
static_assert(typeid(fn<int>()) != typeid(fn<double>()));
Solutions
template <int N>
struct types {
friend auto get(types<N>);
};
template <int N, class T>
struct set_type {
friend auto get(types<N>) { return T{}; }
static constexpr auto value = true;
};
template <int N>
struct te : decltype(get(types<N>{})) {};
template <int N, class T>
[[nodiscard]] constexpr auto typename_cast(const T &v) {
static_assert(set_type<N, T>::value);
return static_cast<te<N>>(v);
}
template <class...>
struct foo {};
template <class... Ts>
constexpr auto fn() {
foo<Ts...> f{};
constexpr std::string_view str = __PRETTY_FUNCTION__;
constexpr auto hash =
std::accumulate(str.begin(), str.end(), 0,
[](auto acc, auto c) { return (acc << CHAR_BIT) ^ c; });
return typename_cast<hash>(f);
}
template <auto Key>
struct Type_name {
friend auto lookup(Type_name);
template <typename T>
struct Store {
friend auto lookup(Type_name) { return T{}; }
};
};
template <auto Key>
struct Erased : decltype(lookup(Type_name<Key>())) {};
template <auto Key, class T>
[[nodiscard]] constexpr auto typename_cast(T&& x) {
void(typename Type_name<Key>::template Store<std::remove_cvref_t<T>>{});
return static_cast<Erased<Key>>(x);
}
template <class...>
struct foo {};
template <typename... Ts>
constexpr std::size_t key() {
std::size_t result{};
for (auto c : __PRETTY_FUNCTION__) (result ^= c) <<= 1;
return result;
}
static_assert(key<int>() != key<double>());
template <class... Ts>
constexpr auto fn() {
foo<Ts...> f{};
return typename_cast<key<Ts...>()>(f);
}
template <auto Str>
struct types final {
friend auto get(types);
template <class T>
struct set {
friend auto get(types) { return T{}; }
};
};
template <auto Str>
struct te : decltype(get(types<Str>{})) {};
template <class T, auto N>
[[nodiscard]] constexpr auto typename_cast(T&& t) {
void(typename types<N>::template set<T>{});
return static_cast<te<N>>(std::forward<T>(t));
}
template <class...>
struct foo {};
template <class... Ts>
constexpr auto fn() {
foo<Ts...> f{};
return typename_cast<decltype(f), (sizeof(Ts) + ... + 0)>(std::move(f));
}
template <typename T>
constexpr size_t comptime_typeid() {
constexpr std::string_view func_name = __PRETTY_FUNCTION__;
// djb2 string hashing
size_t hash = 5381;
for (char c : func_name) {
hash = ((hash << 5) + hash) + c;
}
return hash;
}
template <size_t N>
struct types final {
friend auto get(types<N>);
template <class T>
struct set {
friend auto get(types<N>) { return T{}; }
};
};
template <size_t N>
struct te : decltype(get(types<N>{})) {};
template <size_t N, class T>
[[nodiscard]] constexpr auto typename_cast(T&& t) {
void(typename types<N>::template set<T>{});
return static_cast<te<N>>(t);
}
template <class...>
struct foo {};
template <class... Ts>
constexpr auto fn() {
foo<Ts...> f{};
constexpr auto id = comptime_typeid<decltype(f)>();
return typename_cast<id>(std::move(f));