เรียกใช้ฟังก์ชัน C จากรหัส C ++


93

ฉันมีฟังก์ชัน C ที่ฉันต้องการเรียกใช้จาก C ++ ฉันไม่สามารถใช้extern "C" void foo()วิธีการแบบ "" ได้เนื่องจากฟังก์ชัน C ไม่สามารถคอมไพล์โดยใช้ g ++ แต่มันรวบรวมได้ดีโดยใช้ gcc มีความคิดอย่างไรในการเรียกใช้ฟังก์ชันจาก C ++?


1
คุณช่วยเขียนโค้ดตัวอย่างและg++ข้อความแสดงข้อผิดพลาดได้ไหม
Matthieu Rouget

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 ได้อย่างไร
Chris Stratton

คำตอบ:


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 จุดดี. บางคน schmear C ++ บางใน C ของพวกเขาโดยการตัดในส่วนหัวกับextern "C" #ifdef __cplusplus
ผ่อนคลายใน

@Arne ดูคำตอบของฉันด้านล่าง ผ่อนคลายอย่างที่คุณเห็นฉันเป็นหนึ่งในนั้น;)
gx_

1
ขอบคุณมาก ! นั่นมีประโยชน์มากสำหรับฉัน :)
Hesham Eraqi

ฉันได้รับข้อผิดพลาดต่อไปนี้: ข้อผิดพลาด: # 337: ข้อกำหนดการเชื่อมโยงไม่เข้ากันกับ "foo" ก่อนหน้า (ประกาศที่บรรทัดที่ 1) ตอนนี้การคอมไพล์เรียบร้อยแล้ว ใครช่วยอธิบายหน่อย
FaizanHussainRabbani

@FaizanRabbani ไม่ได้มีรายละเอียดมากขึ้น
ศ. Falken

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

ฉันเห็นด้วยกับคำตอบของศาสตราจารย์ Falkenแต่หลังจากความคิดเห็นของ Arne Mertz ฉันต้องการยกตัวอย่างที่สมบูรณ์ (ส่วนที่สำคัญที่สุดคือ#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
    // ...
}

จากนั้นคุณทำตามคำแนะนำของศาสตราจารย์ Falken เพื่อรวบรวมและเชื่อมโยง

ใช้งานได้เนื่องจากเมื่อคอมไพล์ด้วยgccมาโคร__cplusplusจะไม่ถูกกำหนดดังนั้นส่วนหัวที่somecode.hรวมอยู่ในsomecode.cจะเป็นแบบนี้หลังจากการประมวลผลล่วงหน้า:

void foo(void);

และเมื่อรวบรวมด้วยg++แล้ว__cplusplus จะกำหนดและเพื่อให้ส่วนหัวที่รวมอยู่ในothercode.cppขณะนี้เป็นที่ต้องการที่:

extern "C" {

void foo(void);

}

4
ฉันไม่ชอบ#ifdef __cplusplusรหัสใน C รหัส C เป็นระดับที่ต่ำกว่าและไม่ควรกังวลว่าอาจถูกเรียกจากรหัส C ++ ในบางวัน Imo ที่#ifdefมีการใช้งานในโค้ด C ++ เท่านั้นหากคุณต้องการจัดเตรียมส่วนหัวการผูก C สำหรับไลบรารีที่เขียนด้วย C ++ ไม่ใช่ในทางกลับกัน
Arne Mertz

2
@ Profalken แน่นอน แต่เป็นคำจำกัดความที่สามารถให้ความเข้ากันได้ของโค้ด C ++ "ขาลง" ไม่ใช่สำหรับรหัส C
Arne Mertz

1

คำตอบนี้ได้รับแรงบันดาลใจจากกรณีที่เหตุผลของ Arne ถูกต้อง ผู้ขายเขียนไลบรารีซึ่งเคยรองรับทั้ง C และ C ++ อย่างไรก็ตามเวอร์ชันล่าสุดรองรับเฉพาะ C. คำสั่งร่องรอยต่อไปนี้ที่เหลืออยู่ในรหัสทำให้เข้าใจผิด:

#ifdef __cplusplus
extern "C" {
#endif

สิ่งนี้ทำให้ฉันต้องใช้เวลาหลายชั่วโมงในการรวบรวมใน C ++ เพียงแค่เรียก C จาก C ++ ก็ง่ายกว่ามาก

อนุสัญญา ifdef __cplusplus ละเมิดหลักการความรับผิดชอบเดียว รหัสที่ใช้หลักการนี้พยายามทำสองสิ่งพร้อมกัน:

  • (1) เรียกใช้ฟังก์ชันใน C - และ -
  • (2) เรียกใช้ฟังก์ชันเดียวกันใน C ++

มันเหมือนกับการพยายามเขียนทั้งภาษาอังกฤษแบบอเมริกันและอังกฤษในเวลาเดียวกัน นี่เป็นการโยน #ifdef __thequeensenglish ประแจ #elif __yankeeenglish โดยไม่จำเป็น #else เครื่องมือที่ไร้ประโยชน์ซึ่งทำให้โค้ดอ่าน #endif ลงในโค้ดได้ยากขึ้น

สำหรับโค้ดอย่างง่ายและไลบรารีขนาดเล็กอาจใช้หลักการ ifdef __cplusplus ได้ อย่างไรก็ตามสำหรับไลบรารีที่ซับซ้อนควรเลือกภาษาหนึ่งหรืออีกภาษาหนึ่งและยึดติดกับมัน การสนับสนุนภาษาใดภาษาหนึ่งจะใช้เวลาดูแลรักษาน้อยกว่าการพยายามสนับสนุนทั้งสองภาษา

นี่คือบันทึกการแก้ไขที่ฉันทำกับโค้ดของ Arne เพื่อให้คอมไพล์บน 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.