Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing dynamic functor to stl

I am relatively new to c++ and this is my very first post, so be gentle ;-)

I understand how to pass function pointers or function objects, like many stl functions require. It is unclear to me how to use this in combination with inheritance.

Specifically I would like to call a stl function with several functors that inherit from a base functor. I have not found any relevant remarks or best practices on this topic. Here a simplified code snippet:

struct generator{
  virtual int operator()() = 0;
  virtual ~generator(){};
};

struct A : public generator{
  int operator()();
};

struct B : public generator{
  int operator()();
};

int main()
{
  auto v = std::vector<std::array<int, 500>>(2);
  std::array<std::shared_ptr<generator>, 2> functors = {{std::make_shared<A>(),
                                                         std::make_shared<B>()}};
  for (size_t i = 0; i < functors.size(); ++i)
    std::generate(std::begin(v[i]), std::end(v[i]),
                  *(functors[i]));
  return 0;
}

Compiling this gives:

clang++ -std=c++11 -g -Wall -Wextra test_stackoverflow.cpp 
test_stackoverflow.cpp:25:5: error: no matching function for call to 'generate'
    std::generate(std::begin(v[i]), std::end(v[i]),*(functors[i]));
    ^~~~~~~~~~~~~
/usr/lib/gcc/i686-linux-gnu/4.6/../../../../include/c++/4.6/bits/stl_algo.h:5003:5: note: candidate template ignored: substitution
      failure [with _ForwardIterator = int *, _Generator = generator]: parameter type 'generator' is an abstract class
    generate(_ForwardIterator __first, _ForwardIterator __last,

Yes the type of *(functors[i]) is generator. Which will not allow for dynamic access to A::operator() and B::operator(). I understand how to (rudamentally) solve this. e.g.

std::generate(std::begin(v[i]), std::end(v[i], 
              [&]{ return functors[i]->operator()(); };

However this seems rather obfuscated and I'm looking for a clearer way to achieve my goals.

How does one usually pass such dynamic functors. Or if this is frowned upon, what is the best alternative?

Any help or suggestions are most welcome.

like image 464
APevar Avatar asked Nov 09 '22 19:11

APevar


1 Answers

The problem, as you may already know, is that std::generate takes the generator by value. To pass a polymorphic object, you must pass by reference.

And there is a solution for exactly this kind of problem in c++11 (besides the lambda option, which is still more elegant than what we had before c++11, I believe). The solution is std::ref. It returns a reference wrapper that can be passed by value.

This should work for you:

std::generate(std::begin(v[i]), std::end(v[i]),
              std::ref(*(functors[i])));

The reason why you can pass reference wrappers as a functor, is that they implement the operator()

like image 153
eerorika Avatar answered Nov 14 '22 22:11

eerorika