Extract value from expression "cascading ifs"

Suppose I want to create my own lambda key with the following syntax:

auto s = make_switch(std::pair{0, []{ return 0; }}, std::pair{1, []{ return 50; }}, std::pair{2, []{ return 100; }}); assert( s(0) == 0 ); assert( s(1) == 50 ); assert( s(2) == 100 ); 

I would like to use the fold expression to have a concise implementation that does not require recursion. The idea is to generate something similar to a bunch of nested if :

 if(x == 0) return 0; if(x == 1) return 50; if(x == 2) return 100; 

I would like to write the following:

 // pseudocode template <typename... Pairs> auto make_switch(Pairs... ps) { return [=](int x) { ( if(ps.first == x) return ps.second(), ... ); }; } 

The above code does not work because if(...){...} not an expression. Then I tried using the && operator:

 template <typename... Pairs> auto make_switch(Pairs... ps) { return [=](int x) { return ((ps.first == x && ps.second()), ...); }; } 

This compiles, but returns the result ps.first == x && ps.second() , which is the bool value, not the int value I want.

I would like some operator to be a combination between the comma operator and && : it should evaluate and evaluate on the right side of the operator if the left side evaluates to true .

I cannot come up with any method that would allow me to implement it in such a way as to get the return value of ps.second() and pass it to the calling lambda element returned by make_switch .

Is it possible to implement such a โ€œcascading if s templateโ€ with the expression fold? I would like to evaluate only as many expressions as required until an appropriate branch is found.

+7
c ++ fold c ++ 17
source share
3 answers

I am surprised that it has not yet been proposed:

 template <typename ...Pairs> auto make_switch(Pairs ...ps) { return [=](int x) { int ret; ((x == ps.first && (void(ret = ps.second()), 1)) || ...) /* || (throw whatever, 1) */ ; return ret; }; } 

(try it online)

An additional variable is required, but apparently the only alternatives are recursion and a wrapper class with an overloaded binary operator, and both look less elegant to me.

Short circuit || used to stop the function when a match is detected.

(For the above code, GCC 7.2 gives me a warning: suggest parentheses around '&&' within '||' . Probably an error?)

Edit:

Here is a version generalized to any type: (@Barry credits for std::optional clause)

 template <typename InputType, typename ReturnType, typename ...Pairs> auto make_switch(Pairs ...ps) { /* You could do * using InputType = std::common_type_t<typename Pairs::first_type...>; * using ReturnType = std::common_type_t<decltype(ps.second())...>; * instead of using template parameters. */ return [=](InputType x) { std::optional<ReturnType> ret /* (default_value) */; ( ( x == ps.first && (void(ret.emplace(std::move(ps.second()))), 1) ) || ...) /* || (throw whatever, 1) */; return *ret; }; } 

(try it online)

I decided to use template parameters for parameter types and returns, but you can output them if you want.

Note that if you decide not to have a default value or throw , then passing an invalid value to the switch will give you UB.

+14
source share

It's impossible. To use the fold statement, you need to define a binary operator on Pairs .

In your case, such a binary operator cannot exist, because:

  • it has to be executed taking into account the state (i.e. grab x, as it compares Pairs::first with x )
  • and the operator must be either (i) a non-static member function or a function (ii) that is not a member.

Further

  • (i) the non-static member operator implicitly accepts this as its first argument, and you cannot make this pointer to Pairs or derived from Pairs ;
  • (ii) the non-member function could not fix the value of x .
0
source share

I think the solution from HolyBlackCat is better, but ... how about using the amount?

 template <typename ... Pairs> auto make_switch (Pairs ... ps) { return [=](int x) { return ( (ps.first == x ? ps.second() : 0) + ... ); }; } 

Unfortunately, it only works for types where the amount is defined.

0
source share

All Articles