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?
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++.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With