Home > Enterprise >  Variadic construction with per-argument type deduction
Variadic construction with per-argument type deduction

Time:03-16

I have a class Processor that I'd like to be able to, using C 14 or below (C 17, 20, etc ideas very welcome too for information and posterity, but the problem is specifically C 14):

  • Take a variadic list for construction, each element of which can be:
    • "values" of basically any type, or
    • Handlers: some kind of polymorphic type that has a do_handle member function.
  • Internally convert that heterogeneously-typed list into some kind of container of only Handlers, using the relevant "value" constructor for "values", and just copying things that come in as Handlers
  • Which can later be iterated over with some other context (in the below case it's an int, but that could be anything, e.g. other Processor state).
// Handler base class
class Handler {
public:
    // "handle" something, somehow
    virtual int handle_thing(int foo) const = 0;
}

// Imagine there are "some" Handler derived classes of various kinds
// and say we construct by some factories which look something roughly like the following.
// Factories aren't critical, what's important is you can
// dispatch a given type to a suitable `Handler` implementation

// Factory: copy-constructs a copy of the given handler
// (may use a std::enable_if, whatever)
template<typename SomeHandler>
std::unique_ptr<Handler> make_handler(SomeHandler handler) {
   return std::make_unique<SomeHandler>(handler);
}

// Factory: create a handler for some other type
template<typename ValueType>
std::unique_ptr<Handler> make_handler(ValueType value) {
   retun std::make_unique<ValueHandler<ValueType>>(value);
}

template<typename ...Types>
class Processor {
public:
    // Some number of 
    Processor(Types&&... args) {
        // Something like this pseudo-code:
        // for (arg in args) {
        //     // Construct some suitable handler somehow
        //     m_handlers.push_back(make_handler(arg));
        // }
    }

    // use the handlers somehow (accumulation here is just an example)
    int do_process(int foo) {
        int acc = 0;
        for (const auto& handler: m_handlers) {
            acc  = handler->handle_thing(foo);
        }
        return acc;
    }

private:
    // Some kind of container of per-argument handlers
    // Polymorphism probably indicates container-of-unique_ptr 
    // but not specifically needed to be so
    std::vector<std::unique_ptr<Handler>> m_handlers;
}

My basic problem is that I don't know how to go from the heterogenous construction arguments of types Types&&... to an element-by-element construction of a Handler based on the type of each element.

I have tried to construct a std::tuple<Types...>, but that doesn't really seem to help me very much, since it's not obvious how to sensible iterate that and get the resulting Handler container.

I have also thought about constructing the m_handlers container like this:

std::vector<Handler*, sizeof...(params)> list {args...};

But since the individual handlers are actually different types, it's not obvious to me that that can be done.

CodePudding user response:

In C 11 and C 14 you can use brace-enclosed initializers to expand and handle variadic arguments. So in your case you could do

    Processor(Types&&... args) {
        int dummy[] = { (m_handlers.push_back(make_handler(args)), 0)... };
    }

Since C 17 you can also use a fold expression with the comma operator to achieve the same effect:

    Processor(Types&&... args) {
        (m_handlers.push_back(make_handler(args)), ...);
    }
  • Related