Reputation: 97
I have an application that is creating maps for civilization V. As an interesting design choice I decided to create a couple of functions that would do the looping through the map for me. This way I could pass a function pointer or a lambda function to that function that goes through the whole map doing something to each tile. Reasoning behind this was if I or someone else would change the way the map is stored (from 2D array to a 2D vector or whatever) one would only need to change one function instead of the whole codebase.
Now the problem, here is some code first.
Error code.
case ALL_SNOW:
m.loop_through_limit([] (Tile* t) {
t = new Snow(t->get_x(), t->get_y());
return t;
}, x, y, width, height);
break;
case PTN_ONE:
m.loop_through_limit([&] (Tile* t) {
int cur_x = t->get_x();
int cur_y = t->get_y();
t = new Plains(cur_x, cur_y);
// if (y <= height/4 || y >= (height*3)/4) {
// Top quarter rows and bottom quarter rows
// t = new Ocean(cur_x, cur_y);
// } else if (cur_x <= width/4) {
// Leftmost columns
// t = new Ocean(cur_x, cur_y);
// } else if (cur_x >= (width*3)/4) {
// Rightmost columns
// t = new Desert(cur_x, cur_y);
// }
return t;
}, x, y, width, height);
break;
Definitions from header file.
void loop_through(void (*)(Tile* t));
void loop_through_limit(Tile* (*)(Tile* t), int start_x, int start_y, int width, int height);
Now the difference in each case isn't much apart from the commented out code. This works fine. When I comment out that if statement block, then this is my output.
c++ -c -g -O3 -ffast-math -Wall -Weffc++ -std=c++0x -o tile_block.o tile_block.cpp
tile_block.cpp: In static member function ‘static void TileBlock::write(Map&, TileBlock::Patterns, int, int, int, int)’:
tile_block.cpp:82:35: error: no matching function for call to ‘Map::loop_through_limit(TileBlock::write(Map&, TileBlock::Patterns, int, int, int, int)::<lambda(Tile*)>, int&, int&, int&, int&)’
tile_block.cpp:82:35: note: candidate is:
map.h:26:10: note: void Map::loop_through_limit(Tile* (*)(Tile*), int, int, int, int)
map.h:26:10: note: no known conversion for argument 1 from ‘TileBlock::write(Map&, TileBlock::Patterns, int, int, int, int)::<lambda(Tile*)>’ to ‘Tile* (*)(Tile*)’
And I believe the problem comes when I start using the parameters I'm trying to capture by reference. Then it starts to turn into a "lambda" function instead of just a "function pointer", maybe I'm just not getting it.
Any suggestions?
Upvotes: 1
Views: 954
Reputation: 20191
Lambdas are typically implemented as functors (objects with an overloaded operator()
). For lambas without captures the standard guarantees that they are implicitely convertible to a function pointer with the same signature (safe, since the lambda functor doesn't contain data). For lambdas with capture that is not safely possible and therefore forbidden.
In order to allow for this you need to change your loop_through
and loop_through_limit
method to either take std::function<void(Tile*)>
:
void loop_through(std::function<void(Tile*)>);
void loop_through_limit(std::function<Tile*(Tile*)> func, int start_x, int start_y, int width, int height);
or to a template function taking any type of executable function object
template<typename F> void loop_through_limit(F func);
template<typename F> void loop_through_limit(F func, int start_x, int start_y, int width, int height);
The later approach has the advantage of lower overhead (no need to construct a std::function
object), while the former approach has the advantage of not making the method a template, so it can e.g. still be virtual.
Upvotes: 2
Reputation: 361342
C++11 lambda are not function pointers if they capture variables. What you need is called std::function
, especially for the second function, because the lambda for that capture variables.
So change these:
void loop_through(void (*)(Tile* t));
void loop_through_limit(Tile* (*)(Tile* t), /*...*/);
to these:
void loop_through(std::function<void(Tile*)> fun);
void loop_through_limit(std::function<Tile*(Tile*)> fun, /*...*/);
Now you can pass lambda to the above functions.
Upvotes: 5
Reputation: 13486
...Then it starts to turn into a "lambda" function instead of just a "function pointer"...
Thats exactly right, Standard says that lambdas that do not capture anything can be implicitly cast to function pointers with same signature.
That you can do is make loop_through
and loop_through_limit
templates
template <typename F>
void loop_through(F);
template <typename F>
void loop_through_limit(F, int start_x, int start_y, int width, int height);
and call f
inside.
Upvotes: 1