Splatting a struct
Asked Answered
B

1

6

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?

Blockhead answered 14/6, 2019 at 2:16 Comment(6)
What is the function supposed to do? What problem is it supposed to solve? Going from a structure to a parameter pack is not possible, but you can go from a structure to a std::tuple as well from a paremeter pack to a std::tuple. But to access members of a std::tuple you need to use compile-time constants, you can't easily iterate over the "elements" of the tuple.Bareilly
And wouldn't it be better to solve it through simple overloading? Possibly using overloading to call the parameter pack function (if you must use it)?Bareilly
There is a way in C++17, where you convert a struct to a tuple that contains all struct members, and then you pass the tuple. It's ugly though. I would redesign the current approach if I were you.Heterogynous
What about just passing a ref or good forbid pointer to the structure?Discouragement
Have you considered (if not impractical or otherwise unwanted) to refactor those structs as templates foo<D> with an array member of size D and some free functions a(foo), b(foo)?Impetus
That is certainly the best solution... however I do not own the structs :(Blockhead
C
1

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++.

Corrade answered 14/6, 2019 at 12:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.