10

I'm playing around with C++11 right now and found the following problem with using a lambda as callback to sqlite. When capturing a vector variable inside the lambda, I get an error saying that the signatures don't match. Without using that variable in the lambda ([] instead of [&ret], and not using ret inside), it works fine.

vector<SomeClass> ret;
char *err = nullptr;
int res = sqlite3_exec(db,
                       "some sql query, doesn't matter",
                       [&ret](void *unused, int argc, char **argv, char **columnName) -> int
                       {
                           ret.push_back(SomeClass());
                           return 0;
                       },
                       nullptr,
                       &err);

This is the error I get:

cannot convert 'TestClass::testMethod()::<lambda(void*, int, char**, char**)>' to 'int (*)(void*, int, char**, char**)' for argument '3' to 'int sqlite3_exec(sqlite3*, const char*, int (*)(void*, int, char**, char**), void*, char**)'

GCC version is "gcc (XvidVideo.RU - GCC 4.6.1 i686-pc-mingw32) 4.6.1 20110625 (prerelease)" on Windows.

Why does this make a difference?

AndiDog
  • 68,631
  • 21
  • 159
  • 205
  • I'm trying the same thing with the visual studio 2010 compiler and it doesn't like either form of the lambda... Does anyone know if this is a known bug in the VS2010 impl? – Jamie Cook Dec 15 '11 at 22:34

2 Answers2

10

Only captureless lambdas can be converted to pointers to function, and, based on the compiler diagnostic, your sqlite3_exec expects such a pointer, int (*)(void*, int, char**, char**).

To quote §5.1.2[expr.prim.lambda]/6

The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator.

Cubbi
  • 46,567
  • 13
  • 103
  • 169
  • Makes sense. Just wanted to know if I can solve this with captured variables in any way. (In this special case I can of course use sqlite's callback variable for my purposes.) – AndiDog Aug 26 '11 at 21:52
  • @AndiDog I am guessing you'd have to go the way of so many C++ callbacks for C libraries: a non-member or static member function that figures out what to really call based on some `void*`. – Cubbi Aug 26 '11 at 21:58
  • 2
    Yes, `sqlite3_exec` allows me to pass a `void*` to the callback, I just naively thought this could be done easier with C++11. – AndiDog Aug 26 '11 at 22:00
2

what about using the 1st argument for callback?

vector<SomeClass> ret;
char *err = nullptr;
int res = sqlite3_exec(db,
                       "some sql query, doesn't matter",
                       [](void *ctx, int argc, char **argv, char **columnName) -> int
                       {
                           static_cast<vector<SomeClass>*>(ctx)->push_back(SomeClass());
                           return 0;
                       },
                       &ret,
                       &err);
pepero
  • 7,095
  • 7
  • 41
  • 72