Skip to content

Latest commit

 

History

History
207 lines (166 loc) · 5.04 KB

257.md

File metadata and controls

207 lines (166 loc) · 5.04 KB
Info

Example

struct foo {
  int a{};
  int b{};
};

struct bar {
  int a{};
};

struct missing_a {
  int b{};
};

struct row_with_member_a {
  constexpr explicit(false) row_with_member_a(const auto& t)
    : a{t.a}
  { }

  int a{};
};

auto shrink(row_with_member_a r) {
  std::cout << r.a;
}

int main() {
  //shrink(missing_a{.b = 42}); // error
  shrink(foo{.a = 4, .b = 2}); // prints 4
  shrink(bar{.a = 42}); // prints 42
}

https://godbolt.org/z/arxnx1xaj

Puzzle

  • Can you implement a generic shrink function which uses row polymorphism technique to shrink input type and returns string output?
template <fixed_string Name, class TValue = void*>
struct row {
  static constexpr auto name = Name;
  TValue value{};
  template <class T>
  constexpr auto operator=(const T& t) {
    return row<Name, T>{.value = t};
  }
};

template <fixed_string Name>
constexpr auto operator""_row() {
  return row<Name>{};
}

template <class... Ts>
struct rows; // TODO

auto shrink(rows<row<"a", int>, row<"b", int>> t) {
  return std::to_string(t["a"_row]) + ", " + std::to_string(t["b"_row]);
}

struct empty {};

struct nope {
  int a{};
};

struct foo {
  int a{};
  int b{};
};

struct bar {
  int c{};
  int b{};
  int a{};
};

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

  static_assert(not [](auto t) { return requires { shrink(t); };}(empty{}));
  static_assert(not [](auto t) { return requires { shrink(t); }; }(nope{}));
  static_assert([](auto t) { return requires { shrink(t); }; }(foo{}));
  static_assert([](auto t) { return requires { shrink(t); }; }(bar{}));

  using std::literals::string_literals::operator""s;
  expect("1, 2"s == shrink(foo{.a = 1, .b = 2}));
  expect("2, 4"s == shrink(bar{.c = 8, .b = 4, .a = 2}));
}

https://godbolt.org/z/crGqYjfoT

Solutions

template <class T, fixed_string Name>
concept has_member_named = []<class... Ts>(type_list<Ts...>) {
  return ((std::string_view{meta::get_name_v<Ts>} == Name) or ...);
}(meta::unpack_sequence_t<type_list, meta::get_data_members_t<reflexpr(T)>>{});

template <class T, fixed_string... Names>
concept has_members_named = (has_member_named<T, Names> and ...);

template <class... TRows>
struct rows : TRows... {
  template <has_members_named<TRows::name...> T>
  rows(const T& t) {
    for_each(get_data_members(get_aliased(mirror(T))),
      [&](auto mo) {
        if constexpr (constexpr std::string_view s = std::data(get_name(mo));
                      ((s == TRows::name) or ...)) {
          constexpr fixed_string<std::size(s)> name = std::data(s);
          (*this)[row<name>{}] = get_value(mo, t);
        }
      });
  }

  template <class N>
  constexpr auto operator[](N) -> decltype(auto) {
    return [] <class T> (row<N::name, T>& t) -> auto& {
      return t.value;
    }(*this);
  }
};

https://godbolt.org/z/rj7Wjdxnb

template <class...>
struct rows;

template <class T, fixed_string Name>
concept shrinkable = [] {
  using namespace std::experimental::reflect;

  return []<class... Ts>(std::tuple<Ts...>) {
    return (... or (std::string_view{get_name_v<Ts>} == Name));
  }
  (unpack_sequence_t<std::tuple, get_data_members_t<reflexpr(T)>>{});
}();

template <class... Ts>
struct rows : Ts... {
  template <class T>
  requires(... and shrinkable<T, Ts::name>) constexpr rows(const T &t) {
    for_each(get_data_members(mirror(T)), [&](auto member) {
      if constexpr (constexpr std::string_view name = get_name(member);
                    (... or (name == Ts::name))) {
        using key = row<fixed_string<size(name)>{data(name)}>;
        (*this)[key{}] = get_value(member, t);
      }
    });
  }

  template <fixed_string V>
  auto &operator[](row<V>) {
    return []<class T>(row<V, T> &self) -> T & { return self.value; }(*this);
  }
};

https://godbolt.org/z/xv681zY3r

template <class TData, class... TRows>
concept HasRows = []<class... TMembers>(std::tuple<TMembers...>) {
    return (... and [](std::string_view name) {
        return (... or (name == meta::get_name_v<TMembers>));
    }(TRows::name));
}
(meta::unpack_sequence_t<std::tuple, meta::get_data_members_t<reflexpr(TData)>>{});

template <class... TRows>
struct rows : TRows... {
    template <HasRows<TRows...> TData>
    rows(const TData& data) {
        for_each(get_data_members(mirror(TData)), [&](auto member) {
            constexpr std::string_view name = get_name(member);
            (..., [&] {
                if constexpr (name == TRows::name) {
                    static_cast<TRows&>(*this).value = data.*get_pointer(member);
                }
            }());
        });
    };

    template <fixed_string Name>
    constexpr auto& operator[](row<Name>) {
        return []<class TValue>(row<Name, TValue> & self) -> auto& { return self.value; }
        (*this);
    };
};

https://godbolt.org/z/1fbh4z3bf