从(C)头文件调用C++函数会导致链接器错误
我正在集成一个库 (lwip),我想将日志记录机制重新路由printf到我自己编写的内容(直接记录到我的 uart)。
在某些头文件中存在以下代码
/** Platform specific diagnostic output.n
* Note the default implementation pulls in printf, which may
* in turn pull in a lot of standard libary code. In resource-constrained
* systems, this should be defined to something less resource-consuming.
*/
#ifndef LWIP_PLATFORM_DIAG
#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
#include <stdio.h>
#include <stdlib.h>
#endif
我替换了这条线
#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
和
#define LWIP_PLATFORM_DIAG(x) do {logLineToUart x;} while(0)
我对我的函数使用与签名相同的printf签名:
void logLineToUart(const char * log, ...);
我将该函数放在我自己的标头中lwiplogging.h,我将其包含在定义的标头中LWIP_PLATFORM_DIAG
而且,我在 C++ 文件中实现了该函数:
void logLineToUart(const char * log, ...)
{
uart3.Write(log);
uart3.Write("rn");
}
注意:uart3 是我Uart班级的一个对象。因此它是 C++ 代码。我怀疑这是问题所在,但我不知道如何纠正我的错误。添加extern "C"到函数似乎也不能解决它。
我得到的链接器错误:
Linking .piobuildnucleo_f446zefirmware.elf
.pio/build/nucleo_f446ze/lib997/lwip/api/api_lib.o: In function `netconn_send':
api_lib.c:(.text.netconn_send+0x26): undefined reference to `logLineToUart'
.pio/build/nucleo_f446ze/lib997/lwip/api/sockets.o: In function `tryget_socket_unconn_nouse':
sockets.c:(.text.tryget_socket_unconn_nouse+0xa): undefined reference to `logLineToUart'
.pio/build/nucleo_f446ze/lib997/lwip/api/sockets.o: In function `get_socket':
sockets.c:(.text.get_socket+0x14): undefined reference to `logLineToUart'
.pio/build/nucleo_f446ze/lib997/lwip/api/sockets.o: In function `lwip_connect':
sockets.c:(.text.lwip_connect+0x22): undefined reference to `logLineToUart'
sockets.c:(.text.lwip_connect+0x38): undefined reference to `logLineToUart'
.pio/build/nucleo_f446ze/lib997/lwip/api/sockets.o:sockets.c:(.text.lwip_connect+0x7e): more undefined references to `logLineToUart' follow
collect2.exe: error: ld returned 1 exit status
*** [.piobuildnucleo_f446zefirmware.elf] Error 1
我究竟做错了什么?
更新
将函数声明为 extern "C" 时出现的错误
更新 2:
这确实编译:
#ifdef __cplusplus
extern "C" {
void logLineToUart(const char * log, ...);
#endif
void logLineToUart(const char * log, ...);
#ifdef __cplusplus
}
#endif
回答
extern "C" void logLineToUart(const char * log, ...);
您需要告诉它在第一次看到名称 C-linker 时使其兼容。
更新
在extern "C"如C ++编译时,只有。它不是 C 中的合法语法。您可以单独使用extern(如果您不使用它,则暗示了它)。
社论
这不是很友好...... C++ 通过接受不适用于 C++ 的语法来适应与 C 共享头文件,只是为了便于使用相同的头文件内容。特别是,(void)用于空参数列表(这是“令人厌恶的”)并允许在可变参数函数参数列表中使用逗号(这(int x ...)是 C++ 最初定义它的方式;当相同的功能被合并到 ANSI C 中时,它们使用(int x, ...)了一个多余的逗号,即语法不需要。)C++ 编译器接受这两者,以便更容易地使用 C 头文件......
但是,您必须添加extern "C"所有内容。无论如何,这引入了条件编译,即使 C++ 接受 C 语法。请注意,对于单个声明,如果省略它,extern int foo (int);则extern是允许和隐含的。如果 C 编译器允许链接规范,即使只有“C”可用,它也会使生活更轻松。请注意,在大多数情况下,C 和 C++ 实现是相同的编译器套件,并且通常一种语言支持另一种语言的某些功能作为扩展。如果在 C 模式下gcc支持 etc.会很酷extern "C++",因为该代码库当然知道名称如何对参数进行编码。