HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Most elegant variadic functor

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
elegantfunctorvariadicmost

Problem

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 

template 
std::function
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.

Solution

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

Code Snippets

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();
  };
}
(inputs.set(input_vals),0)...
((void)inputs.set(input_vals),0)...

Context

StackExchange Code Review Q#39750, answer score: 2

Revisions (0)

No revisions yet.