Panggil fungsi C dari kode C ++

90

Saya memiliki fungsi C yang ingin saya panggil dari C ++. Saya tidak bisa menggunakan " extern "C" void foo()" jenis pendekatan karena fungsi C gagal dikompilasi menggunakan g ++. Tapi dikompilasi dengan baik menggunakan gcc. Ada ide bagaimana memanggil fungsi dari C ++?

Dangila
sumber
1
Bisakah Anda menulis beberapa kode contoh dan g++pesan kesalahan
Matthieu Rouget
7
Jika Anda mengompilasinya dengan kompiler C ++, itu adalah C ++. Kode C tidak harus dikompilasi dengan kompiler C ++. Mereka adalah bahasa yang berbeda. Kode Anda bukan C ++ yang valid dan karenanya tidak dapat dikompilasi dengan kompiler C ++.
xaxxon
3
@MatthieuRougetvoid valid_in_C_but_not_in_CPlusPlus(size_t size) { char variable_length_array[size]; }
autis
2
void f(void *pv) { int *pi = pv; *pi = 42; }
Percobaan
1
Ini harus dibiarkan terbuka, terutama karena memiliki jawaban yang bagus yang menunjukkan bagaimana compiler C (bukan C ++) dapat digunakan untuk kode C.
Chris Stratton

Jawaban:

126

Kompilasi kode C seperti ini:

gcc -c -o somecode.o somecode.c

Kemudian kode C ++ seperti ini:

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

Kemudian tautkan keduanya, dengan linker C ++:

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

Anda juga harus memberi tahu compiler C ++ bahwa header C akan muncul saat Anda menyertakan deklarasi untuk fungsi C. Jadi othercode.cppdimulai dengan:

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

somecode.h harus berisi sesuatu seperti:

 #ifndef SOMECODE_H_
 #define SOMECODE_H_

 void foo();

 #endif


(Saya menggunakan gcc dalam contoh ini, tetapi prinsipnya sama untuk semua kompiler. Buat secara terpisah sebagai C dan C ++, masing-masing, lalu tautkan bersama.)

Prof. Falken
sumber
7
@Arti Poin bagus. Beberapa orang merancang beberapa C ++ di C mereka dengan membungkusnya extern "C"di header dengan #ifdef __cplusplus.
bersantai
@Arne Lihat jawaban saya di bawah. bersantai Seperti yang Anda lihat, saya salah satunya;)
gx_
1
Terima kasih banyak ! Itu sangat berguna bagi saya :)
Hesham Eraqi
Saya menerima error berikut: Error: # 337: spesifikasi linkage tidak kompatibel dengan "foo" sebelumnya (dideklarasikan pada baris 1) Sekarang kompilasi oke. Adakah yang bisa menjelaskan?
FaizanHussainRabbani
@FaizanRabbani, bukan tanpa banyak detail.
Prof. Falken
61

Izinkan saya mengumpulkan potongan-potongan dari jawaban dan komentar lain, untuk memberi Anda contoh dengan kode C dan C ++ yang dipisahkan dengan rapi:

Bagian C:

foo.h :

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

#include "foo.h"

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

Kompilasi ini dengan gcc -c -o foo.o foo.c.

Bagian C ++:

bar.cpp

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

void bar() {
  foo();
}

Kompilasi ini dengan g++ -c -o bar.o bar.cpp

Dan kemudian tautkan semuanya:

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

Rasional: Kode C harus kode C biasa, bukan #ifdefuntuk "mungkin suatu hari nanti saya akan memanggil ini dari bahasa lain". Jika beberapa programmer C ++ memanggil fungsi C Anda, itu masalah mereka bagaimana melakukannya, bukan milik Anda. Dan jika Anda adalah programmer C ++, maka header C mungkin bukan milik Anda dan Anda tidak boleh mengubahnya, jadi penanganan nama fungsi yang tidak berubah (yaitu extern "C") termasuk dalam kode C ++ Anda.

Anda dapat, tentu saja, menulis sendiri header C ++ praktis yang tidak melakukan apa pun kecuali menggabungkan header C ke dalam extern "C"deklarasi.

Arne Mertz
sumber
7
Kelihatannya mantap. 1 untuk alasannya
gx_
Akhirnya penjelasan yang sangat jelas tentang ini. Terima kasih banyak!
Daniel Soutar
16

Saya setuju dengan jawaban Prof. Falken , namun setelah komentar Arne Mertz saya ingin memberikan contoh yang lengkap (bagian terpenting adalah #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
    // ...
}

Kemudian Anda mengikuti instruksi Prof. Falken untuk menyusun dan menautkan.

Ini berfungsi karena saat mengompilasi dengan gcc, makro __cplusplustidak ditentukan, jadi header yang somecode.hdisertakan somecode.cseperti ini setelah pemrosesan awal:

void foo(void);

dan ketika kompilasi dengan g++, maka __cplusplus yang didefinisikan, dan header termasuk dalam othercode.cppsekarang seperti itu:

extern "C" {

void foo(void);

}
gx_
sumber
4
thb, saya tidak suka #ifdef __cplusplusdi kode C. Kode C adalah level yang lebih rendah, dan tidak perlu repot jika mungkin dipanggil dari kode C ++ suatu hari nanti. Imo yang #ifdefpenggunaannya hanya dalam kode C ++ jika Anda ingin memberikan header pengikatan C untuk pustaka yang ditulis dalam C ++, bukan sebaliknya.
Arne Mertz
2
@ Prof.Falken tentu saja, tetapi ini adalah definisi yang dimaksudkan untuk dapat memberikan kompatibilitas "ke bawah" dari kode C ++, bukan untuk kode C.
Arne Mertz
1

Jawaban ini diinspirasi oleh kasus di mana alasan Arne benar. Vendor menulis pustaka yang pernah mendukung C dan C ++; namun, versi terbaru hanya mendukung C. Arahan sisa berikut yang tersisa di kode menyesatkan:

#ifdef __cplusplus
extern "C" {
#endif

Ini menghabiskan waktu beberapa jam untuk mencoba mengkompilasi dalam C ++. Cukup menelepon C dari C ++ jauh lebih mudah.

Konvensi ifdef __cplusplus melanggar prinsip tanggung jawab tunggal. Kode yang menggunakan konvensi ini mencoba melakukan dua hal sekaligus:

  • (1) menjalankan fungsi di C - dan -
  • (2) jalankan fungsi yang sama di C ++

Ini seperti mencoba menulis dalam bahasa Inggris Amerika dan Inggris pada saat yang bersamaan. Ini tidak perlu membuang kunci pas #ifdef __thequeensenglish #elif __yankeeenglish wrench #else alat yang tidak berguna yang membuat kode lebih sulit dibaca #endif ke dalam kode.

Untuk kode sederhana dan pustaka kecil, konvensi ifdef __cplusplus dapat berfungsi; Namun, untuk perpustakaan yang kompleks, yang terbaik adalah memilih satu bahasa atau yang lain dan tetap menggunakannya. Mendukung salah satu bahasa akan membutuhkan lebih sedikit perawatan daripada mencoba mendukung keduanya.

Ini adalah catatan modifikasi yang saya buat pada kode Arne agar dapat dikompilasi di 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
Petani
sumber