Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Splatting a struct

I have a bunch of structs like this with increasing number of members, but consistent member naming:

struct one { int a; };
struct two { int a; int b; };
struct three { int a; int b; int c; };

I also have a templated function which I want to have accept one of these struct's members, splatted:

template <typename T, typename ... ARGS> // T will be one, two, or three
void func(ARGS... args); // This should take 1, 2, or 3, int arguments respectively

I want to be able to call this something like:

two foo;

func<two>(splatter(foo));

Where splatter would somehow split foo so that it would resolve to func<two>(foo.a, foo.b).

I can obviously just expand this inline, without splatter, but the code in which I call func is itself happily templated. I've tried using an initializer_list but I can't figure out how to build one based on template type alone.

Unfortunately my compiler also doesn't support constexpr if to splat a call to func or build an initializer_list. Are there any other options available to me?

like image 893
Jonathan Mee Avatar asked Nov 06 '22 16:11

Jonathan Mee


1 Answers

As far as I know, what you describe cannot be done using c++. Or if it can, than it is a very complicated solution. The reason is, that you would need to somehow store pointers to class member access functions and then call them properly with your actual object.

However, you get a similar functionality with overloading, which is much easier to implement. For example, you could define a call_func which you overload for your types:

#include <array>

// types
struct one {
  int a;
};
struct two {
  int a;
  int b;
};
struct three {
  int a;
  int b;
  int c;
};
template <class T>
struct more_complex_type {
  T a;
  T b;
};
// template function
template <typename T, typename... ARGS>
auto func(ARGS... args) {
  return std::array<T, sizeof...(args)>{args...};
}
// indirection overload
template <class T>
struct call_func_impl {};
template <>
struct call_func_impl<one> {
  auto call(one val) { return func<int>(val.a); }
};
template <>
struct call_func_impl<two> {
  auto call(two val) { return func<int>(val.a, val.b); };
};
template <>
struct call_func_impl<three> {
  auto call(three val) { return func<int>(val.a, val.b, val.c); };
};
template <class T>
struct call_func_impl<more_complex_type<T>> {
  auto call(more_complex_type<T> val) { return func<T>(val.a, val.b); };
};
// syntacting sugar
template <class T>
auto call_func(T val) {
  return call_func_impl<T>{}.call(val);
}
// tests
auto test_func() { return func<int>(1, 2, 3, 4, 5); }
auto test_func_of_one() {
  auto val = one{};
  return call_func(val);
}
auto test_func_of_two() {
  auto val = two{};
  return call_func(val);
}
auto test_func_of_three() {
  auto val = three{};
  return call_func(val);
}
auto test_func_of_more_complex_type() {
  auto val = more_complex_type<double>{};
  return call_func(val);
}

This example uses an overloaded struct template to wrap the function call. This might not be necessary for your case, as you do not have templatized types. You actually could just overload call_func. However, this approach allows you to define the call for more_complex_type which is templatized, as partial function overloading is currently not possible in c++.

like image 77
jan.sende Avatar answered Nov 18 '22 17:11

jan.sende