Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterator-reducer pattern for functions that may or may not return a value

Tags:

c++

c++11

The following function applies a functor on each element and reduces the return value:

template <class FCT, class RED>
RED::TYPE forAllElements(FCT functor, RED reducer){
  for(/* all elem in elements */){
    reducer(functor(elem));
  }
  return reducer.value;
}

Now, sometimes I might wish to just call the functor on all elements, and not reduce anything. Basically, I would then like to have something like:

class FunctorThatReturnsNothing{
  void operator() (Elem e){
    // do something, return nothing...
  }
}

class DummyReducer{
  using TYPE = void; // ??? something like that ???

  template <class FCT>
  void operator() (/* ??? what here */){
    // do nothing...
  }
}

forAllElements(FunctorThatReturnsNothing(), DummyReducer());

But that won't compile since I have reducer(functor(elem)) where the non-existent return value of a void function is taken as an argument.

Is there a way to make it work for void functors without duplicating forAllElements for a void and a non-void case?

(For people suspecting an XY-Problem: I basically know different approaches for iterating and reducing and I think that the presented callback approach is appropriate for my case. I just wonder how I can avoid to have duplicate code for the "return value + reducing" and the "just callback" case.)

like image 513
Michael Avatar asked Oct 31 '22 04:10

Michael


1 Answers

I think you can just make a VoidReducer class, but instead of return reducer.value; you would need return reducer.getvalue();. Then you simply make void VoidReducer::getvalue(){}.

I haven't tested this but the idea should work. You are allowed to return f(); if both f and the current function have return type void.

EDIT
Now that I read the question more carefully, I see that the problem you're asking about is the line reducer(functor(elem));.
For this I would compile-time dispatch based on decltype(functor(elem)).

template <class Functor, class Reducer, class Elem>
void Combine(Functor functor, Reducer & reducer, Elem elem, std::true_type)
{
  functor(elem);
}

template <class Functor, class Reducer, class Elem>
void Combine(Functor functor, Reducer & reducer, Elem elem, std::false_type)
{
    reducer(functor(elem));
}

template <class Functor, class Reducer, class Elem>
void Combine(Functor functor, Reducer & reducer, Elem elem)
{
    Combine(functor, reducer, elem, std::is_same<decltype(functor(elem)), void>());
}

Then calling Combine instead of reducer(functor(elem)) will correctly reduce the return value of functor if and only if it's not void.

PS: Sprinkle references and std::forward calls to taste.

like image 137
SirGuy Avatar answered Nov 02 '22 09:11

SirGuy