7
\$\begingroup\$

Question

Suppose we have two sorts of classes

  • an input class Input
    • defines a type result_type
    • defines set(result_type)
  • an output class Output
    • defines a type result_type
    • defines result_type get() const
    • has a number of Input classes as member variables, on which its output depends

Given an output class and several input classes (arbitrary number), consider the following procedure:

  • loop over each input class and call set() with an appropriate value (defined beforehand)
  • call the get() on the ouput class and collect the result.

This procedure can be seen as a call to a function taking the input's values as arguments an returning the output value.

Write the functor that constructs such a variadic function in the general case.

Constraints are: C++ (most likely C++11), arbitrary number of input classes of possibly different Input::result_types. Note that Input::result_type is not necessarily related to Output::result_type. Aim should first be efficiency, but there's a big bonus if the code is elegant and readable.

Details: For those who wonder how Output is related to Input, one could imagine that Input has a result_type get() const method as well, which returns whatever you provided via set(). Output then has a constructor that takes various Inputs, and stores them (or their reference) as member variables. Output::get() then does some math by using the return values of its input's get() methods, and returns some result of type Output::result_type.

Proposed solution

#include <functional>

template <class Output, class... Inputs>
std::function<typename Output::result_type(typename Inputs::result_type...)>
make_function(const Output& output, Inputs&... inputs) {
  return[&](typename Inputs::result_type... input_vals) {
    int dummy[]{0, (inputs.set(input_vals),0)...};
    return output.get();
  };
}

The int dummy[] line is due to @ecatmur's answer.

\$\endgroup\$
  • \$\begingroup\$ Just as a question, how are the arbitrary number of Inputs stored in Output? \$\endgroup\$ – Yuushi Jan 22 '14 at 1:46
  • \$\begingroup\$ there are multiple classes that have a fixed number of inputs, and each store them in a similar way (see details section). \$\endgroup\$ – yannick Jan 22 '14 at 9:22
  • \$\begingroup\$ Ok. I was asking because if they are all the same type, then I think there is a more elegant solution. Having them be heterogeneous types makes it more difficult, however. I might think about it a bit more, but I will say that if you're looking for efficiency, I'd try and avoid std::function if possible. \$\endgroup\$ – Yuushi Jan 24 '14 at 0:01
  • \$\begingroup\$ by what would you replace std::function then? \$\endgroup\$ – yannick Jan 28 '14 at 10:55
  • 2
    \$\begingroup\$ In this case, you likely don't have a choice without changing the design significantly. In C++14, this may change with generic lambdas. That being said, having thought about it, I don't think this can really be improved upon. \$\endgroup\$ – Yuushi Jan 29 '14 at 1:47
2
\$\begingroup\$

Looks to me as if you've already got the most elegant solution in mind.

In C++14 you'd remove the dependency on std::function and simply return a naked lambda, like this:

template <class Output, class... Inputs>
auto make_function(const Output& output, Inputs&... inputs) {
  return [&](typename Inputs::result_type... input_vals) {
    int dummy[] { 0, ((void)inputs.set(input_vals),0)... };
    return output.get();
  };
}

but in C++11 you can't make a function-that-returns-a-naked-lambda without a ton of boilerplate — if it's even possible at all.


Also, a nitpick, with props to @stephan-t-lavavej's talk at CppCon 2014. You wrote

(inputs.set(input_vals),0)...

but what you should have written was

((void)inputs.set(input_vals),0)...

to avoid accidentally calling MaliciousUserCode::operator,(int).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.