Skip to content

Latest commit

 

History

History
99 lines (78 loc) · 2.97 KB

258.md

File metadata and controls

99 lines (78 loc) · 2.97 KB
Info

  • Did you know that static reflection can be used to invoke functions with named parameters?

Example

struct foo {
  auto api(int i) -> void { std::cout << i; }
};

auto main() -> int {
  namespace meta = std::experimental::reflect;
  foo f{};
  (f.*meta::get_pointer_v<meta::get_element_t<0, meta::get_member_functions_t<reflexpr(foo)>>>)(42); // prints 42
}

https://godbolt.org/z/sMeeo981v

Puzzle

  • Can you implement invoke which calls given by name function from reflected struct with given named parameters?
template<fixed_string Name> /*TODO*/ invoke{};

struct foo {
  auto bar(int value) { bar_called_with = value; }
  auto baz(int i, double d) { baz_called_with = {i, d}; }

  std::optional<int> bar_called_with{};
  std::optional<std::pair<int, double>> baz_called_with{};
};

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

  "invoke"_test = [] {
    should("support single value") = [] {
      foo f{};
      invoke<"bar">(f, "value"_arg = 42);
      expect(not f.baz_called_with and f.bar_called_with and *f.bar_called_with == 42_i);
    };

    should("support diff order") = [] {
      foo f{};
      invoke<"baz">(f, "i"_arg = 4,  "d"_arg = 2.0);
      expect(not f.bar_called_with and f.baz_called_with and *f.baz_called_with == std::pair{4, 2.});
    };

    should("support diff order") = [] {
      foo f{};
      invoke<"baz">(f, "d"_arg = 0., "i"_arg = 42);
      expect(not f.bar_called_with and f.baz_called_with and *f.baz_called_with == std::pair{42, .0});
    };
  };
}

https://godbolt.org/z/zTnEeaTxf

Solutions

template<fixed_string Name, class T, class... Args> decltype(auto) invoke(T& t, Args&&...args) {
  const auto get_arg = [&] <class P> {
    return ([&] -> meta::get_reflected_type_t<meta::get_type_t<P>> {
              if constexpr(std::string_view{meta::get_name_v<P>} == Args::name) {
                return args.value;
              } else {
                return {};
              }}() + ...); // exploiting +
  };

  const auto call = [&] <class F> -> bool {
    if constexpr (std::string_view{meta::get_name_v<F>} == Name) {
      const auto f = meta::get_pointer_v<F>;
      using params_t = meta::get_parameters_t<F>;
      [&] <class TParams, auto... Ns> (TParams, std::index_sequence<Ns...>) {
        (t.*f)(get_arg.template operator()<meta::get_element_t<Ns, TParams>>()...);
      }(params_t{}, std::make_index_sequence<meta::get_size_v<params_t>>{});
      return true;
    } else {
      return false;
    }
  };

  using type = meta::get_aliased_t<reflexpr(T)>;
  using member_functions_t = meta::get_member_functions_t<type>;
  std::apply([&] <class... Fs> (Fs...) {
      (call.template operator()<Fs>() or ...);
    }, meta::unpack_sequence_t<std::tuple, member_functions_t>{});
}

https://godbolt.org/z/Ka3xYYd7a