Связывание скомпилированной статической библиотеки C с программой С++

Я попытался связать статическую библиотеку (скомпилированную с gcc) в программу С++, и я получил ссылку undefined. Я использовал gcc и g++ версию 4.6.3 на серверной машине ubuntu 12.04. Например, вот простой файл библиотеки для факториала:

mylib.h

#ifndef __MYLIB_H_
#define __MYLIB_H_

int factorial(int n);

#endif

mylib.c

#include "mylib.h"

int factorial(int n)
{
    return ((n>=1)?(n*factorial(n-1)):1);
}

Я создал объект для этого mylib.c, используя gcc:

gcc -o mylib.o -c mylib.c

Снова статическая библиотека была создана из объектного файла с помощью утилиты AR:

ar -cvq libfact.a mylib.o

Я протестировал эту библиотеку с помощью программы C (test.c) и программы на С++ (test.cpp)

Обе программы C и С++ имеют одно и то же тело:

#include "mylib.h"
int main()
{
    int fact = factorial(5);
    return 0;
}

Предполагая, что статическая библиотека libfact.a доступна в каталоге /home/test, я скомпилировал свою программу на C без каких-либо проблем:

gcc test.c -L/home/test -lfact

Однако при тестировании С++-программы он сбросил ошибку ссылки:

g++ test.cpp -L/home/test -lfact

test.cpp:(.text+0x2f): undefined reference to `factorial(int)'
collect2: ld returned 1 exit status

Я даже попытался добавить команду extern в test.cpp:

extern int factorial(int n) //added just before the main () function

По-прежнему такая же ошибка.

  • Может ли кто-нибудь сказать мне, что я здесь не так?
  • Есть ли что-то, что я пропустил при создании статической библиотеки?
  • Должен ли я добавить что-нибудь в свой test.cpp, чтобы он работал?

Ответ 1

Проблема в том, что вы не сказали своей программе на С++, что факториал написан на C. Вам нужно изменить заголовочный файл test.h. Как этот

#ifndef __MYLIB_H_
#define __MYLIB_H_

#ifdef __cplusplus
extern "C" {
#endif

int factorial(int n);

#ifdef __cplusplus
}
#endif

#endif

Теперь ваш заголовочный файл должен работать как для программ C, так и для С++. Подробнее см. здесь.

Идентификаторы BTW, содержащие двойной знак подчеркивания, зарезервированы для компилятора (так называются имена, начинающиеся с подчеркивания и заглавная буква), поэтому #ifndef __MYLIB_H_ является незаконным, строго говоря. Я бы изменился на #ifndef MYLIB_H #define MYLIB_H

Ответ 2

Хотя принятый ответ абсолютно правильный, я думал, что просто добавлю наблюдение. Некоторые редакторы имеют проблемы с открытой/закрывающей скобкой и будут отступать от всей области extern "C" в заголовке. Если mylib.h является ключевым заголовком для библиотеки, вы можете подумать:

#if defined (__cplusplus)
#define _MYLIB_INIT_DECL extern "C" {
#define _MYLIB_FINI_DECL }
#else
#define _MYLIB_INIT_DECL
#define _MYLIB_FINI_DECL
#endif

Все остальные заголовки в библиотеке mylib, например mylib_aux.h, могут иметь следующий вид:

#ifndef _MYLIB_AUX_H
#define _MYLIB_AUX_H

#include <mylib.h>

_MYLIB_INIT_DECL

... header content ...

_MYLIB_FINI_DECL

#endif /* _MYLIB_AUX_H */

Очевидно, что имена, которые я использую, являются произвольными, но для нескольких заголовков библиотек этот подход мне был полезен.