Info
-
Did you know that you can implement a compile-time map with C++?
Example
static_assert(std::is_same_v<void, boost::mp11::mp_map_find<std::tuple<>, int>>);
static_assert(std::is_same_v<void, boost::mp11::mp_map_find<std::tuple<std::pair<int, double>>, double>>);
static_assert(std::is_same_v<std::pair<int, double>, boost::mp11::mp_map_find<std::tuple<std::pair<int, double>>, int>>);
Puzzle
- Can you implement a user-friendly
make
routine which creates a given type with named parameters by leveraging a compile-time map?
template<template<class...> class T, class... Ts>
constexpr auto make(Ts...) { /*TODO*/ }
template<class TSize, class TValue>
struct foo {};
static_assert(std::is_same_v<foo<int, int>, decltype(make<foo>("size"_arg = int{}, "value"_arg = int{}))>);
static_assert(std::is_same_v<foo<int, int>, decltype(make<foo>("value"_arg = int{}, "size"_arg = int{}))>);
static_assert(std::is_same_v<foo<short, double>, decltype(make<foo>("size"_arg = short{}, "value"_arg = double{}))>);
Solutions
template <auto... Cs>
struct Arg {
template <typename T>
constexpr auto operator=(const T&) { return std::pair<Arg<Cs...>, T>{}; }
};
template <typename T, T... Cs>
constexpr auto operator""_arg() {
return Arg<Cs...>{};
}
template<template<typename...> typename T, typename... Ts>
constexpr auto make(Ts...) {
using map_t = boost::mp11::mp_list<Ts...>;
using size_t = typename boost::mp11::mp_map_find<map_t, decltype("size"_arg)>::second_type;
using value_t = typename boost::mp11::mp_map_find<map_t, decltype("value"_arg)>::second_type;
return T<size_t, value_t>{};
}
template< typename K>
struct KeyValue
{
template< typename V>
auto operator = ( V )
{
return std::pair<K,V>{};
}
};
template<std::size_t N>
struct StaticString
{
char str[N]{};
constexpr StaticString ( char const(&p_str)[N] )
{
std::ranges::copy(p_str, str);
};
template<std::size_t M>
constexpr bool operator == ( char const(&p_str)[M] ) const
{
return std::ranges::equal( p_str, str);
}
};
struct value_type{};
struct size_type{};
template<StaticString ss>
constexpr auto operator""_arg()
{
if constexpr(ss == "value")
return KeyValue<value_type>{};
else if constexpr( ss == "size" )
return KeyValue<size_type>{};
}
template<template<class...> class T, class... Ts>
constexpr auto make(Ts...) {
return T< typename boost::mp11::mp_map_find<std::tuple<Ts...>, size_type>::second_type
, typename boost::mp11::mp_map_find<std::tuple<Ts...>, value_type>::second_type
>{};
}
template<auto Size>
struct fixed_string {
char data[Size + 1]{};
static constexpr auto size = Size;
constexpr explicit(false) fixed_string(char const* str) { std::copy_n(str, Size + 1, data); }
constexpr explicit(false) operator std::string_view() const { return {data, Size}; }
};
template<auto Size> fixed_string(char const (&)[Size]) -> fixed_string<Size - 1>;
template<auto...>
struct arg {
template<class T> constexpr auto operator=(const T& t) const { return std::pair<arg, T>{*this, t}; }
};
template<fixed_string Str> constexpr auto operator""_arg() {
return []<auto... Ns>(std::index_sequence<Ns...>) {
return arg<Str.data[Ns]...>{};
}(std::make_index_sequence<Str.size>{});
}
template<template<class...> class T, class... Ts>
constexpr auto make(Ts...) {
using types_t = boost::mp11::mp_list<Ts...>;
using size_t = typename boost::mp11::mp_map_find<types_t, decltype("siize"_arg)>::second_type;
using value_t = typename boost::mp11::mp_map_find<types_t, decltype("value"_arg)>::second_type;
return T<size_t, value_t>{};
}
template<char... Cs> struct Name;
template<typename T, char ... Cs>
struct NamedType {
using name = Name<Cs...>;
using type = T;
};
template<char ...Cs>
struct Name {
template<typename T> NamedType<T, Cs...> operator=(T) { return {}; }
};
template<typename Tchar, Tchar ... Cs> constexpr Name<Cs...> operator"" _arg() { return {}; }
template<template<class...> class T, class... Ts>
constexpr auto make(Ts...) {
using map = std::tuple<std::pair<typename Ts::name, typename Ts::type>...>;
using value = boost::mp11::mp_map_find<map, Name<'v','a','l','u','e'>>::second_type;
using size = boost::mp11::mp_map_find<map, Name<'s','i','z','e'>>::second_type;
return T<size,value>{};
}
namespace detail
{
using boost::mp11::mp_second;
using boost::mp11::mp_map_find;
template<auto Size>
struct fixed_string {
using type = fixed_string;
char data[Size + 1]{};
static constexpr auto size = Size;
constexpr fixed_string() = default;
constexpr fixed_string(char const* str) { std::copy_n(str, Size + 1, data); }
};
template<auto Size> fixed_string(char const (&)[Size]) -> fixed_string<Size - 1>;
template<auto...>
struct arg
{
template <class T>
constexpr std::pair<arg, T> operator=(T&& value) const { return {*this, value}; }
};
template<template<class...> class T>
struct class_template {};
template<template<class...> class T, template<class... Ts> class M>
concept is_same_class_template_v = std::is_same_v<class_template<T>, class_template<M>>;
namespace literal
{
template<fixed_string Fs> constexpr auto operator""_arg()
{
return []<auto... Ns>(std::index_sequence<Ns...>)
{
return arg<Fs.data[Ns]...>{};
}(std::make_index_sequence<Fs.size>{});
}
template<fixed_string Fs> constexpr auto make_arg()
{
return []<auto... Ns>(std::index_sequence<Ns...>)
{
return arg<Fs.data[Ns]...>{};
}(std::make_index_sequence<Fs.size>{});
}
}
// for simplifying user-defined declarations below
namespace helpers
{
template<template<class...> class T, template<class... Ts> class M>
concept match = is_same_class_template_v<T, M>;
template<template <class...> class T, class L, fixed_string... Ks>
using unpack = T<typename mp_map_find<L, decltype(literal::make_arg<Ks>())>::second_type...>;
template<class... Ts>
using pack = std::tuple<Ts...>;
}
}
using namespace detail::literal;
using namespace detail::helpers;
// user-defined template classes
template<class TSize, class TValue>
struct foo {};
template<class TOne, class TTwo>
struct bar {};
// user-defined declarations to define unpacking order of named arguments for template classes
template<template<class...> class T, class... Ts> requires match<T, foo>
constexpr unpack<T, pack<Ts...>, "size", "value"> make(Ts...) { return {}; }
template<template<class...> class T, class... Ts> requires match<T, bar>
constexpr unpack<T, pack<Ts...>, "one", "two"> make(Ts...) { return {}; }