如何将sqlite3及其扩展函数静态链接到C/C++应用程序中?

3
我已经将sqlite3.c编译到我的应用程序中,将extension-functions.c(hxxps://www.sqlite.org/contrib)编译成共享库,并且它可以正常工作,但是对于我正在处理的项目,我必须将sqlite3 + extension functions静态链接到一个C++应用程序中。 我已经阅读了很多关于这个主题的资料,但是我无法让它正常工作。
最小示例(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;
}

构建命令:
gcc -o test sqlite3.c extension_functions.c test.cc -lpthread -lm -ldl -DSQLITE_CORE

"

(-DSQLITE_CORE没有影响吗?)

错误:

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

使用注释行代替:
/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

我使用的其他来源:

  • hxxp://books.google.de/books?vid=ISBN1449399649&q=209
  • hxxps://www.sqlite.org/loadext.html
  • hxxp://stackoverflow.com/a/1295765
也许我太糊涂了,但是测试.cc中给出了函数原型,而extension_functions.c中给出了函数定义。有人知道这个代码示例有什么问题吗?感谢您的帮助。(对于hxxp://链接,我很抱歉,但我不允许发布超过两个链接)

你需要提供更多来源。例如,在您的test.cc中没有说明sqlite3_extension_functions_init()是一个C函数(它会认为是C ++),而鉴于您的命令行,我假设它在extension_functions.c中,因此在test.cc中声明时需要使用extern "C" { ... } - Karel Kubat
我成功地将sqlite 3.8.7的“合并”编译到我的程序中。正如指出的那样,链接器找不到这个函数。似乎你需要查看为该函数定义的调用约定。通常有“API”。调用约定指定了如何链接,并且更改符号表中的名称——这可能会导致未定义的引用。 - Joe
@KarelKubat:谢谢您。这就是解决方案。我之前没有混合使用C和C++,因此不知道extern "C" { ... }的可能性。此外,我希望我的最小示例是纯C代码,但当然... gcc将*.cc文件编译为C++代码,而我的实际程序也是C++。因此,一个可运行的最小示例可以使用gcc ... test.c ...gcc -x c ...,或在C++情况下使用extern "C" { ... }。如果您发布答案,我会接受它。 :) - Striker
@Striker,谢谢并很高兴我能帮到你。我添加了一个带有更多背景的答案。祝你编码愉快! - Karel Kubat
2个回答

3

在混合使用C和C++代码时,确保在C++端将C函数的声明标记为C,例如:

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

编译器会为以C或C++声明的外部变量生成不同的符号。错误信息也会反映出这一点:
test.cc:(.text+0x10): undefined reference to `sqlite3_extension_functions_init(sqlite3*, char**, sqlite3_api_routines const*)'

在这种情况下,编译器准备了一个符号名称,因为它认为正在处理一个C++函数,而实际上您的函数位于extension_functions.c中将是一个C函数,链接器应该期望解析不同的符号名称。此错误消息说明链接器无法解析基于C++命名约定的符号名称。
顺便提一下,C++支持函数重载,而C不支持。因此,在C++中,您可以拥有两个不同的函数:
extern void foobar(int x, int y);
extern void foobar(char const *x);

为了区别它们,C++编译器通常通过将函数原型作为符号名的一部分来分配不同的符号给这些不同的函数。相比之下,一个C函数如int foobar(),无论具有什么函数参数,都只会产生符号foobar而没有符号名中的参数原型,因为C语言没有函数重载。

这就是为什么声明C函数的头文件必须可用于C和C++的原因。通常结构如下:

#ifdef __cplusplus
extern "C" {
#endif
    /* Now follow declarations of C functions, such as extern int printf()
     * or whatever 
     */
#ifdef __cplusplus
}
#endif

1
请勿将C语言中“链接”的概念与将编译的函数集合物理“链接”成完整程序的过程混淆。后者是逻辑上与源代码编译分开的操作,尽管受前者影响。
预处理器宏可以影响您的代码如何编译,但不能(直接)影响它如何链接。您的扩展函数可能需要使用-DSQLITE_CORE进行编译(尽管我对此不太自信),但您需要链接选项来构建一个单片机可执行文件。您还需要SQLite库的静态版本。
GCC提供了一个通用的前端(gcc)用于编译和链接,所以您需要做的主要事情是(在链接阶段)向gcc传递一个指示它执行静态链接的选项。该选项拼写为-static

感谢这些澄清。我主要在使用Autotools的项目中工作,因此还有基本的编译器知识/选项的空间。由于这个答案不是我的问题的解决方案(请参见Karel Kubat的评论),所以我不能接受它,但您会得到一个赞,因为它对我有帮助。编辑:啊,我不能给你的答案点赞,因为我的声望太低了,抱歉。 :( - Striker
@striker,既然你提到了Autotools,如果你正在使用Libtool,那么你的configure脚本应该已经为你提供了选项“--enable-static”和“--disable-shared”,你应该使用它们。 - John Bollinger
你可能无法为答案点赞,但如果你对它感到满意,可以通过点击旁边的勾选标记来“接受”它。 - John Bollinger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接