Вызов функции C из кода C ++

93

У меня есть функция C, которую я хотел бы вызвать из C ++. Я не мог использовать " extern "C" void foo()" подход, потому что функция C не могла быть скомпилирована с использованием g ++. Но он отлично компилируется с использованием gcc. Есть идеи, как вызвать функцию из C ++?

Дангила
источник
1
Не могли бы вы написать пример кода и g++сообщений об ошибках
Матье Руже,
7
Если вы скомпилируете его с помощью компилятора C ++, это C ++. Код C не обязательно компилировать с помощью компилятора C ++. Это разные языки. Ваш код недействителен C ++ и поэтому не компилируется с компилятором C ++.
xaxxon
3
@MatthieuRougetvoid valid_in_C_but_not_in_CPlusPlus(size_t size) { char variable_length_array[size]; }
аутист
2
Моя попытка: void f(void *pv) { int *pi = pv; *pi = 42; }^^
gx_
1
Это следует оставить открытым, тем более что в нем есть хорошие ответы, указывающие, как компилятор C (а не C ++) может использоваться для кода C.
Крис Стрэттон,

Ответы:

130

Скомпилируйте код C следующим образом:

gcc -c -o somecode.o somecode.c

Затем код C ++ выглядит так:

g++ -c -o othercode.o othercode.cpp

Затем свяжите их вместе с помощью компоновщика C ++:

g++ -o yourprogram somecode.o othercode.o

Вы также должны сообщить компилятору C ++, что идет заголовок C, когда вы включаете объявление для функции C. Итак, othercode.cppначинается с:

extern "C" {
#include "somecode.h"
}

somecode.h должен содержать что-то вроде:

 #ifndef SOMECODE_H_
 #define SOMECODE_H_

 void foo();

 #endif


(В этом примере я использовал gcc, но принцип тот же для любого компилятора. Собирайте отдельно как C и C ++ соответственно, а затем связывайте их вместе.)

Проф. Фалькен
источник
7
@Arne Хорошие замечания. Некоторые люди используют C ++ в своих C, заключая extern "C"в заголовок с #ifdef __cplusplus.
расслабьтесь
@Arne См. Мой ответ ниже. раскрутите Как видите, я один из них;)
gx_
1
Спасибо огромное ! Это было очень полезно для меня :)
Hesham Eraqi
Я получал следующую ошибку: Ошибка: # 337: спецификация связывания несовместима с предыдущим "foo" (объявленным в строке 1). Теперь его компиляция в порядке. Кто-нибудь может объяснить?
FaizanHussainRabbani
@FaizanRabbani, не без подробностей.
Проф. Фалькен
63

Позвольте мне собрать отрывки из других ответов и комментариев, чтобы дать вам пример с четко разделенным кодом C и C ++:

Часть C:

foo.h :

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

#include "foo.h"

void foo(void)
{
    /* ... */
}

Скомпилируйте это с помощью gcc -c -o foo.o foo.c.

Часть C ++:

bar.cpp

extern "C" {
  #include "foo.h" //a C header, so wrap it in extern "C" 
}

void bar() {
  foo();
}

Скомпилируйте это с g++ -c -o bar.o bar.cpp

А затем свяжите все вместе:

g++ -o myfoobar foo.o bar.o

Обоснование: Код C должен быть простым кодом C, а не #ifdef«может быть, когда-нибудь я назову это с другого языка». Если какой-то программист на C ++ вызывает ваши C-функции, это их проблема, как это сделать, а не ваша. А если вы программист на C ++, то заголовок C может не принадлежать вам, и вы не должны его изменять, поэтому обработка имен функций без привязки (т. Е. extern "C") Принадлежит вашему коду C ++.

Вы, конечно, можете написать себе удобный заголовок C ++, который ничего не делает, кроме обертывания заголовка C в extern "C"объявлении.

Арне Мерц
источник
8
Выглядит вполне законно. +1 за обоснование
gx_
Наконец-то совершенно четкое объяснение этому. Благодаря тонну!
Daniel Soutar
16

Я согласен с ответом профессора Фалькена , но после комментария Арне Мертца я хочу привести полный пример (самая важная часть - это #ifdef __cplusplus):

somecode.h

#ifndef H_SOMECODE
#define H_SOMECODE

#ifdef __cplusplus
extern "C" {
#endif

void foo(void);

#ifdef __cplusplus
}
#endif

#endif /* H_SOMECODE */

somecode.c

#include "somecode.h"

void foo(void)
{
    /* ... */
}

othercode.hpp

#ifndef HPP_OTHERCODE
#define HPP_OTHERCODE

void bar();

#endif /* HPP_OTHERCODE */

othercode.cpp

#include "othercode.hpp"
#include "somecode.h"

void bar()
{
    foo(); // call C function
    // ...
}

Затем вы следуете инструкциям профессора Фалькена по компиляции и компоновке.

Это работает, потому что при компиляции gccмакрос __cplusplusне определен, поэтому после предварительной обработки заголовок, somecode.hвключенный в него, somecode.cвыглядит следующим образом:

void foo(void);

и при компиляции с g++, то __cplusplus это определяется, и поэтому заголовок включен в othercode.cppнастоящее время так:

extern "C" {

void foo(void);

}
gx_
источник
4
thb, мне не нравится #ifdef __cplusplusкод на C. Код C - это нижний уровень, и ему не нужно беспокоиться, если он когда-нибудь будет вызван из кода C ++. Imo, #ifdefкоторый используется только в коде C ++, если вы хотите предоставить заголовок привязки C для библиотеки, написанной на C ++, а не наоборот.
Арне Мертц
2
@ Prof.Falken, конечно, но это определение предназначено для обеспечения «нисходящей» совместимости кода C ++, а не кода C.
Арне Мертц
1

Этот ответ навеян случаем, когда аргументация Арне была правильной. Поставщик написал библиотеку, которая когда-то поддерживала как C, так и C ++; однако последняя версия поддерживала только C. Следующие рудиментарные директивы, оставленные в коде, вводили в заблуждение:

#ifdef __cplusplus
extern "C" {
#endif

Это стоило мне нескольких часов, пытаясь скомпилировать на C ++. Просто вызвать C из C ++ было намного проще.

Соглашение ifdef __cplusplus нарушает принцип единой ответственности. Код, использующий это соглашение, пытается делать две вещи одновременно:

  • (1) выполнить функцию в C - и -
  • (2) выполнить ту же функцию на C ++

Это все равно, что пытаться писать одновременно на американском и британском английском. Это излишне бросает #ifdef __thequeensenglish гаечный ключ #elif __yankeeenglish wrench # еще один бесполезный инструмент, который затрудняет чтение кода #endif в коде.

Для простого кода и небольших библиотек может работать соглашение ifdef __cplusplus; однако для сложных библиотек лучше выбрать тот или иной язык и придерживаться его. Поддержка одного из языков потребует меньшего обслуживания, чем попытка поддержки обоих.

Это запись изменений, которые я внес в код Арне, чтобы он компилировался в Ubuntu Linux.

foo.h :

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

#include "foo.h"
#include <stdio.h>

void foo(void)
{
     // modified to verify the code was called
     printf("This Hello World was called in C++ and written in C\n");
}

bar.cpp

extern "C" {
    #include "foo.h" //a C header, so wrap it in extern "C" 
}

int main() {
  foo();
  return(0);
}

Makefile

# -*- MakeFile -*-
# dont forget to use tabs, not spaces for indents
# to use simple copy this file in the same directory and type 'make'

myfoobar: bar.o foo.o
    g++ -o myfoobar foo.o bar.o 

bar.o: bar.cpp
    g++ -c -o bar.o bar.cpp

foo.o: foo.c
    gcc -c -o foo.o foo.c
Земледелец
источник