0

I need to refactor the following code (simplified for clarity) :

#include <iostream> // those structs cannot be changed struct Foo1 {int f;}; struct Foo2 {int g;}; struct Foo3 {int h;}; void processFoo(Foo1 &foo) { } void processFoo(Foo2 &foo) { } void processFoo(Foo3 &foo) { } bool selectFoo2() { // known at run time return true; } bool selectFoo3() { // known at run time return false; } template<typename ...FooT> void save(const FooT &...foos) { std::cout << "foos = " << sizeof...(foos) << std::endl; } void handle_foos() { // Foo1 is always selected Foo1 f1; processFoo(f1); if (selectFoo2()) { Foo2 f2; processFoo(f2); if (selectFoo3()) { Foo3 f3; processFoo(f3); save(f1, f2, f3); } else save(f1, f2); } else if (selectFoo3()) { Foo3 f3; processFoo(f3); save(f1, f3); } else save(f1); } int main(int, char**) { handle_foos(); return 0; } 

The handle_foos function works as expected with only selected types being passed as arguments to the variadic save function.

However, the code will eventually become less readable with new types (not so many types though) and all those nested if for all combinations of types in the save call.

It is not possible to change the structs definition and the save function.

Basically, i would like to have only one call the the save function with arguments being selected types only. I am looking for a C++14 solution.

I am thinking about storing types that are always selected (known at compile time) in a std::tuple and then using std::tuple_cat to dynamically add selected types at runtime. Then convert the tuple into a parameter pack and pass it to the save function.

I am not sure it's a good idea, is there something more elegant or efficient ?

Thank you.

17
  • 4
    Added the c++ tag because the base language tag should always be added. Commented Jul 12 at 10:33
  • 1
    What you're most likely is looking for is template parameter packs together with std::forward. Commented Jul 12 at 10:36
  • 1
    note that your tuple_cat method I think must fail – each kind of tuple is still its own type, and hence a compile time thing. If you want to register types at run time, you will need a runtime variable container, not a tuple. Which means that your type information must be stored as explicit member of the variant type you're building. Paramter pack unpacking is still a compile-time thing! Commented Jul 12 at 12:25
  • 1
    Have save take pointers rather than references. Teach it to skip nulls. Then handle_foos becomes straightforward. Commented Jul 12 at 13:48
  • 1
    you are thinking of something like this godbolt.org/z/1h8Wxn9hj it generated around 3000 lines of assembly for just 3 types, and it will grow exponentially the more types you add, (note you need to implement std::apply yourself in C++14), it will probably generate around 1 MB worth of code before you reach 10 types Commented Jul 12 at 15:00

1 Answer 1

4

One way to use template is:

 // Allow to dispatch from the Type directly, to be generic template <typename T> bool selectFooT(); //template <> bool selectFooT<Foo1>() { return true; } template <> bool selectFooT<Foo2>() { return selectFoo2(); } template <> bool selectFooT<Foo3>() { return selectFoo3(); } template <typename... Ts> struct handle_foos_impl; // End case: All `Ts` has been processed, now save them. template <> struct handle_foos_impl<> { template <typename... Foos> void operator()(const Foos&... foos) const { save(foos...); } }; template <typename Foo, typename... Ts> struct handle_foos_impl<Foo, Ts...> { template <typename... Foos> void operator()(const Foos&... foos) const { if (selectFooT<Foo>()) { Foo foo; processFoo(foo); handle_foos_impl<Ts...>{}(foos..., foo); } else { handle_foos_impl<Ts...>{}(foos...); } } }; void handle_foos() { // Foo1 is always selected Foo1 f1; processFoo(f1); handle_foos_impl<Foo2, Foo3>{}(f1); } 

Demo (note demo is not in C++14, just for display with std::source_location, but code is C++14 compatible)

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.