3

I already build sqlite3.c into my application and extension-functions.c (hxxps://www.sqlite.org/contrib) into a shared library and it worked fine, but for the project I'm working on I have to statically link sqlite3 + extension functions into a C++ application. I already read a lot of sources to this topic, but I cannot get it working.

Minimal example (test.cc):

#include "sqlite3.h"
#include<cstdlib>

void sqlite3_extension_functions_init(void);
// typedef struct sqlite3_api_routines sqlite3_api_routines;
// int sqlite3_extension_functions_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi);

int main(int argc,char *argv[]) {
    //  sqlite3_auto_extension( (void(*)(void))sqlite3_extension_functions_init );
    sqlite3_auto_extension( sqlite3_extension_functions_init );

    sqlite3 *database_;
    sqlite3_open_v2("test.db", &database_, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);

    sqlite3_close_v2(database_);

    return 0;
}

Build command:

gcc -o test sqlite3.c extension_functions.c test.cc -lpthread -lm -ldl -DSQLITE_CORE

(-DSQLITE_CORE no influence ?)

Errors:

/tmp/ccPfaDLE.o: In function `main':
test.cc:(.text+0x10): undefined reference to `sqlite3_extension_functions_init()'
collect2: ld returned 1 exit status

using commented lines instead:

/tmp/ccrs9B0K.o: In function `main':
test.cc:(.text+0x10): undefined reference to `sqlite3_extension_functions_init(sqlite3*, char**, sqlite3_api_routines const*)'
collect2: ld returned 1 exit status

Other sources I used:

  • hxxp://books.google.de/books?vid=ISBN1449399649&q=209
  • hxxps://www.sqlite.org/loadext.html
  • hxxp://stackoverflow.com/a/1295765

Maybe I'm blind, but the function prototype is given in test.cc and the function definition in extension_functions.c. Has anybody an idea what is wrong with this code example? Thank you in advance for your help.(I'm sorry for the hxxp:// links, but I'm not allowed to post more than two links)

Striker
  • 33
  • 1
  • 4
  • You'll have to provide more sources. E.g., in your `test.cc` it doesn't say that `sqlite3_extension_functions_init()` is a C function (it will assume C++) while given your command line, I assume that it's in `extension_functions.c` and hence will require `extern "C" { ... }` when declared in `test.cc`. – Karel Kubat Mar 04 '15 at 14:15
  • I compile the "amalgamation" of sqlite, 3.8.7, directly into my program successfully. As pointed out, the linker can not find the function. It seems like you need to look at the calling conventions defined for the function. Usually has "API". Calling conventions specifiy how to link and also change the name in the symbol table -- which can lead to undefined references. – Joe Mar 04 '15 at 15:12
  • @KarelKubat: Thank You. This is the solution. I didn't mix C and C++ before and therefor didn't know of the `extern "C" { ... }` possibility. Furthermore I wanted my minimal example to be pure C code, but of course ... gcc compiles *.cc files as C++ code and my actual program is also C++. So a working minimal example would be using `gcc ... test.c ...` or `gcc -x c ...` or `extern "C" { ... }` in the C++ case. If you post an answer I will accept it. :) – Striker Mar 04 '15 at 15:59
  • @Striker, thanks and glad I could help. I added an answer with some more background. Have fun coding! – Karel Kubat Mar 05 '15 at 10:39

2 Answers2

3

When mixing C and C++ code, make sure that on the C++ side the declaration of C functions is marked as being C, as in:

extern "C" {
    int sqlite3_extension_functions_init(sqlite3 *db, char **pzErrMsg, 
                                         const sqlite3_api_routines *pApi);
    // more declarations
}

The compiler produces different symbols for externals that are declared as C or as C++. The error messages reflect that:

test.cc:(.text+0x10): undefined reference to `sqlite3_extension_functions_init(sqlite3*, char**, sqlite3_api_routines const*)'

In this case the compiler prepared a symbol name because it thought it was dealing with a C++ function, while in fact your function that resides in extension_functions.c will be a C function, and the linker should expect to resolve a different symbol name. This error message states that the linker can't resolve the symbol name that's based on C++ nomenclature.

The background btw. is that C++ supports function overloading, while C doesn't. So in C++ you can have two different functions:

extern void foobar(int x, int y);
extern void foobar(char const *x);

In order to distinguish, the C++ compiler assigns different symbols to these different functions, usually by making the prototype of the function arguments part of the symbol name. In contrast, a C function like int foobar() with whatever function arguments will just produce the symbol foobarwithout the argument prototypes in the symbol name, because simply C doesn't have function overloading.

That's incidentally why header files that declare C functions, but must be usable for C and C++, are usually structured as follows:

#ifdef __cplusplus
extern "C" {
#endif
    /* Now follow declarations of C functions, such as extern int printf()
     * or whatever 
     */
#ifdef __cplusplus
}
#endif
Karel Kubat
  • 1,635
  • 11
  • 12
1

Do not confuse the C language concept of "linkage" with the process of physically "linking" a collection of compiled functions into a complete program. The latter is a logically separate operation from source compilation, albeit one influenced by the former.

Preprocessor macros can affect how your code is compiled, but not (directly) how it is linked. Your extension functions perhaps need to be compiled with -DSQLITE_CORE (though I am not confident of that), but you need link options to build a monolithic executable. You also need static versions of the SQLite libraries.

GCC provides a common front end (gcc) for compiling and linking, so the main thing you need to do is (at the linking stage) to pass gcc an option directing it to perform static linking. That option is spelled -static.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Thank you for these clarifications. I worked mostly in projects with Autotools so there is still room for basic compiler knowledge / options. Since this answer is not the solution to my question (see the comment of Karel Kubat) I cannot accept it, but you get an upvote because it was helpful for me. Edit: Ahhh, I cannot upvote your answer because my reputation is too low, sorry. :( – Striker Mar 04 '15 at 16:14
  • @striker, since you mentioned the Autotools, if you are using Libtool then your configure script should already offer you options `--enable-static` and `--disable-shared`, which you should use. – John Bollinger Mar 04 '15 at 17:15
  • And you may not be able to upvote the answer, but if you're satisfied with it then you can *accept* it by clicking the check mark next to it. – John Bollinger Mar 04 '15 at 17:17