Skip to content

Latest commit

 

History

History
259 lines (210 loc) · 7.49 KB

229.md

File metadata and controls

259 lines (210 loc) · 7.49 KB
Info

Example

from collections import namedtuple

nt = namedtuple("price", "size")

nt.price = 42;
nt.size = 100;

assert 42 == nt.price and 100 == nt.size

Puzzle

  • Can you implement a C++ version of named tuples?
template<class... Ts>
struct namedtuple; // TODO

int main() {
  using namespace boost::ut;

  "named tuple"_test = [] {
    should("allow empty") = [] {
      const auto nt = namedtuple{};
      expect(not [](auto t) { return requires { t[""_t]; }; }(nt));
    };

    should("support direct initialization") = [] {
      const auto nt = namedtuple{"price"_t = 42, "size"_t = 100};
      expect([](auto t) { return requires { t["price"_t]; }; }(nt));
      expect([](auto t) { return requires { t["size"_t]; }; }(nt));
      expect(not [](auto t) { return requires { t["quantity"_t]; }; }(nt));
      expect(42_i == nt["price"_t] and 100_i == nt["size"_t]);
    };

    should("support modification") = [] {
      auto nt = namedtuple{"price"_t = int{}, "size"_t = std::size_t{}};
      nt["price"_t] = 12;
      nt["size"_t] = 34u;
      expect(12_i == nt["price"_t] and 34_u == nt["size"_t]);
    };
  };
}

https://godbolt.org/z/c918qhjWP

Solutions

template <char... Cs>
struct named_field {
private:
  static constexpr char name_storage[] = {Cs...};

  template <class T>
  struct value_wrapper {
    static constexpr auto name() { return named_field::name; }
    T value{};
  };

public:
  static constexpr auto name = std::string_view{std::data(name_storage), std::size(name_storage)};

  template <class T>
  constexpr auto operator=(const T& t) { return value_wrapper<T>{.value = t}; }
};

template <class TChar, TChar... Cs> constexpr auto operator""_t () {
  return named_field<Cs...>{};
}

template <class... Ts>
struct namedtuple : Ts... {
private:
  struct no_value {};
  friend constexpr auto operator,(no_value v, no_value) { return v; }
  template <class T> friend constexpr auto operator,(no_value, T&& t) -> decltype(auto) { return std::forward<T>(t); }
  template <class T> friend constexpr auto operator,(T&&t, no_value) -> decltype(auto) { return std::forward<T>(t); }

  // need P0847 "deducing this"
  template <class... Us, char... Cs>
  constexpr auto lookup(const named_field<Cs...>&) const -> decltype(auto) {
    return ([&] <class U> () -> decltype(auto) {
      if constexpr (U::name() == named_field<Cs...>::name) {
        return (static_cast<U&>(const_cast<namedtuple&>(*this)).value);
      } else {
        return no_value{};
      }
    }.template operator()<Us>(), ... , no_value{});
  }

public:
  constexpr auto operator[](const auto& f) const -> decltype(auto)
    requires (not std::is_same_v<decltype(lookup<std::add_const_t<Ts>...>(f)), no_value>)
  {
    return lookup<std::add_const_t<Ts>...>(f);
  }

  constexpr auto operator[](const auto& f) -> decltype(auto)
    requires (not std::is_same_v<decltype(lookup<Ts...>(f)), no_value>)
  {
    return lookup<Ts...>(f);
  }
};

https://godbolt.org/z/6bYhx8EPo

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""_t() {
  return []<auto... Ns>(std::index_sequence<Ns...>) {
    return arg<Str.data[Ns]...>{};
  }(std::make_index_sequence<Str.size>{});
}

template<class... Ts>
struct namedtuple : std::tuple<Ts...> {
  using std::tuple<Ts...>::tuple;

  template<class T> constexpr decltype(auto) operator[](const T) const
    requires(not std::is_void_v<boost::mp11::mp_map_find<boost::mp11::mp_list<Ts...>, T>>)
  {
    using type = boost::mp11::mp_map_find<boost::mp11::mp_list<Ts...>, T>;
    return std::get<type>(*this).second;
  }

  template<class T> constexpr auto& operator[](const T)
    requires(not std::is_void_v<boost::mp11::mp_map_find<boost::mp11::mp_list<Ts...>, T>>)
  {
    using type = boost::mp11::mp_map_find<boost::mp11::mp_list<Ts...>, T>;
    return std::get<type>(*this).second;
  }
};
template<class... Ts> namedtuple(Ts&&...) -> namedtuple<Ts...>;

https://godbolt.org/z/q6EPsnY4P

template <class CharT, CharT... s>
struct field : private boost::mp11::mp_list_c<CharT, s...>
{
    template <class T>
    struct mapped_type {
        using key_type = field;

        constexpr mapped_type(T&& value) : value{value} {}

        constexpr operator auto() { return value; }
        constexpr operator auto() const { return value; }
    private:
        T value;
    };

    auto operator=(auto&& rhs) { return mapped_type{std::forward<decltype(rhs)>(rhs)}; }
};

template <class CharT, CharT... s>
constexpr auto operator""_t() { return field<CharT, s...>{}; }

template <class... Ts>
concept is_set = boost::mp11::mp_is_set<boost::mp11::mp_list<Ts...>>::value;

template <class K, class M>
concept map_contains = boost::mp11::mp_map_contains<M, K>::value;

template <class M, class K>
using map_find = boost::mp11::mp_second<boost::mp11::mp_map_find<M, std::remove_cvref_t<K>>>;

template<class... Ts> requires is_set<Ts...>
class namedtuple : Ts...
{
    using map_type = boost::mp11::mp_list<boost::mp11::mp_list<typename Ts::key_type, Ts>...>;
public:
    constexpr namedtuple(Ts&&... pairs) : Ts{pairs}... {}

    constexpr auto& operator[](const map_contains<map_type> auto& k) {
        return *static_cast<map_find<map_type, decltype(k)>*>(this);
    }
    constexpr auto operator[](const map_contains<map_type> auto& k) const {
        return *static_cast<const map_find<map_type, decltype(k)>*>(this);
    }
};

template<class... Ts> explicit namedtuple(Ts&&...) -> namedtuple<Ts...>;

https://godbolt.org/z/6rq354Yn1

template<char ... Cs>
struct KeyType
{
    template< typename T>
    constexpr auto operator = ( T const & v )
    {
        return std::make_pair( *this, v );
    }
};

template<class T, T ... Cs  >
constexpr auto operator"" _t()
{
    return KeyType<Cs ... >{};
}

template<class... Ts>
struct namedtuple{
    using M = boost::mp11::mp_list<Ts...>;
    static_assert( boost::mp11::mp_is_map<M>::value );//unique keys
    namedtuple( Ts ... args ):data{args...}{}
    template <char ... Cs >
    static constexpr bool Contain( KeyType<Cs...> const & )
    {
        if(sizeof ... (Cs) == 0 || sizeof...(Ts) == 0 ) return false;
        using V= boost::mp11::mp_map_find<M,KeyType<Cs...>>;
        return !std::is_void_v<V>;
    }
    template<char ... Cs > requires ( Contain( KeyType<Cs...>{} ) )
    constexpr auto const & operator[]( KeyType<Cs...> const & ) const
    {
        using V= boost::mp11::mp_map_find<M,KeyType<Cs...> >;
        return std::get<V>(data).second;
    }
    template<char ... Cs > requires  ( Contain( KeyType<Cs...>{} ) )
    constexpr auto & operator[]( KeyType<Cs...> const & )
    {
        using V= boost::mp11::mp_map_find<M,KeyType<Cs...> >;
        return std::get<V>(data).second;
    }
    std::tuple< Ts ... > data;
};

https://godbolt.org/z/v4Kb3K47j