2010-04-30 51 views
48

Tôi biết điều này.Cách gọi hàm C++ từ C?

gọi hàm C từ C++:

Nếu ứng dụng của tôi là trong C++ và tôi đã phải gọi các hàm từ một thư viện được viết bằng C. Sau đó, tôi đã sử dụng

//main.cpp 

extern "C" void C_library_function(int x, int y);//prototype 
C_library_function(2,4);// directly using it. 

wouldn này' T mangle tên C_library_function và linker sẽ tìm thấy cùng một tên trong tập tin * .lib đầu vào của nó và vấn đề được giải quyết.

Gọi hàm C++ từ C ???

Nhưng ở đây tôi đang mở rộng một ứng dụng lớn được viết bằng C và tôi cần sử dụng thư viện được viết bằng C++. Tên mangling của C++ đang gây rắc rối ở đây. Linker đang phàn nàn về các ký hiệu chưa được giải quyết. Vâng tôi không thể sử dụng trình biên dịch C + + trên dự án C của tôi bởi vì thats phá vỡ rất nhiều thứ khác. đường ra là gì?

Bằng cách này tôi đang sử dụng MSVC

+4

Đọc http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html#faq-32.6 – jweyrich

+0

Khi bạn kiểm soát thư viện C++: http: // stackoverflow. com/questions/12615683/calling-c-functions-from-c-file –

+2

Có thể trùng lặp của [Elegantly call C++ from C] (http://stackoverflow.com/questions/7281441/elegantly-call-c-from-c) – user2284570

Trả lời

59

Bạn cần tạo một API C để hiển thị chức năng của mã C++ của bạn. Về cơ bản, bạn sẽ cần phải viết mã C++ được khai báo bên ngoài "C" và có một API C thuần túy (không sử dụng các lớp, ví dụ) kết thúc tốt đẹp thư viện C++. Sau đó, bạn sử dụng thư viện trình bao bọc C thuần túy mà bạn đã tạo.

API C của bạn có thể tùy ý theo một kiểu hướng đối tượng, mặc dù C không hướng đối tượng. Ví dụ:

// *.h file 
// ... 
#ifdef __cplusplus 
#define EXTERNC extern "C" 
#else 
#define EXTERNC 
#endif 

typedef void* mylibrary_mytype_t; 

EXTERNC mylibrary_mytype_t mylibrary_mytype_init(); 
EXTERNC void mylibrary_mytype_destroy(mylibrary_mytype_t mytype); 
EXTERNC void mylibrary_mytype_doit(mylibrary_mytype_t self, int param); 

#undef EXTERNC 
// ... 


// *.cpp file 
mylibrary_mytype_t mylibrary_mytype_init() { 
    return new MyType; 
} 

void mylibrary_mytype_destroy(mylibrary_mytype_t untyped_ptr) { 
    MyType* typed_ptr = static_cast<MyType*>(untyped_ptr); 
    delete typed_ptr; 
} 

void mylibrary_mytype_doit(mylibrary_mytype_t untyped_self, int param) { 
    MyType* typed_self = static_cast<MyType*>(untyped_self); 
    typed_self->doIt(param); 
} 
+0

cảm ơn !! hiểu rồi. http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html#faq-32.6 – claws

+1

Cảm ơn câu trả lời. Tuy nhiên, liên kết bị hỏng. – Milo

+1

Cảm ơn. Đã không nhận ra rằng vẫn còn liên kết đến trang đó từ StackOverlow khi tôi xóa nó. Tôi đã cập nhật câu trả lời để cung cấp thêm chi tiết mà không cần tham khảo trang đó. –

2

xuất khẩu C chức năng của mình ++ như extern "C" (aka C những biểu tượng phong cách), hoặc sử dụng các định dạng file .def để xác định những biểu tượng xuất khẩu undecorated cho C++ mối liên kết khi nó tạo thư viện C++, khi đó trình liên kết C sẽ không gặp khó khăn khi đọc nó

5

Giả sử cáC++ API C là C-tương thích (không lớp, mẫu, vv), bạn có thể bọc nó trong extern "C" { ... }, cũng giống như bạn đã làm khi đi theo cách khác.

Nếu bạn muốn hiển thị các đối tượng và các công cụ C++ dễ thương khác, bạn sẽ phải viết một API trình bao bọc.

+0

Không hoàn toàn ... thư viện C++ sẽ cần phải được biên dịch lại. –

+0

Ồ không. C++ API hoàn toàn hướng đối tượng. – claws

+2

@claws, xem bài viết của tôi về cách tạo mã OOP kiểu C .. và tạo thư viện trình bao bọc bằng cách sử dụng kiểu đó với giao diện C, nhưng thực thi C++ cơ bản. Sau đó, liên kết với giao diện C. –

5

Bạn sẽ phải viết trình bao bọc cho C trong C++ nếu bạn muốn thực hiện việc này. C++ tương thích ngược, nhưng C không tương thích về phía trước.

17

tôi sẽ làm điều đó theo cách sau:

(Nếu làm việc với MSVC, bỏ qua các lệnh GCC biên soạn)

Giả sử rằng tôi có một ++ lớp C tên AAA, được định nghĩa trong file aaa .h, aaa.cpp và lớp AAA có phương thức có tên sayHi (const char * name), mà tôi muốn bật cho mã C.

Các mã C++ lớp AAA - Pure C++, tôi không sửa đổi nó:

// aaa.h

#ifndef AAA_H 
#define AAA_H 

class AAA { 
    public: 
     AAA(); 
     void sayHi(const char *name); 
}; 

#endif 

// aaa.cpp

#include <iostream> 

#include "aaa.h" 

AAA::AAA() { 
} 

void AAA::sayHi(const char *name) { 
    std::cout << "Hi " << name << std::endl; 
} 

Biên soạn lớp này như thường xuyên làm cho C++. Mã này "không biết" rằng nó sẽ được sử dụng bởi mã C. Sử dụng lệnh:

g++ -fpic -shared aaa.cpp -o libaaa.so 

Bây giờ, cũng trong C++, tạo ra một kết nối C. Xác định nó trong các tệp aaa_c_connector.h, aaa_c_connector.cpp. kết nối này sẽ định nghĩa một hàm C, tên AAA_sayHi (cosnt char * name), mà sẽ sử dụng một thể hiện của AAA và sẽ gọi phương thức của nó:

// aaa_c_connector.h

#ifndef AAA_C_CONNECTOR_H 
#define AAA_C_CONNECTOR_H 

#ifdef __cplusplus 
extern "C" { 
#endif 

void AAA_sayHi(const char *name); 

#ifdef __cplusplus 
} 
#endif 


#endif 

// aaa_c_connector.cpp

#include <cstdlib> 

#include "aaa_c_connector.h" 
#include "aaa.h" 

#ifdef __cplusplus 
extern "C" { 
#endif 

// Inside this "extern C" block, I can define C functions that are able to call C++ code 

static AAA *AAA_instance = NULL; 

void lazyAAA() { 
    if (AAA_instance == NULL) { 
     AAA_instance = new AAA(); 
    } 
} 

void AAA_sayHi(const char *name) { 
    lazyAAA(); 
    AAA_instance->sayHi(name); 
} 

#ifdef __cplusplus 
} 
#endif 

biên soạn nó, một lần nữa, bằng cách sử dụng C++ thường lệnh biên soạn:

g++ -fpic -shared aaa_c_connector.cpp -L. -laaa -o libaaa_c_connector.so 

Bây giờ tôi có một thư viện chia sẻ (libaaa_c_connector.so), mà thực hiện chức năng C AAA_sayHi (const char * name). bây giờ tôi có thể tạo một tập tin chính C và biên dịch nó tất cả cùng nhau:

// main.c

#include "aaa_c_connector.h" 

int main() { 
    AAA_sayHi("David"); 
    AAA_sayHi("James"); 

    return 0; 
} 

Biên soạn nó bằng cách sử dụng lệnh C biên soạn:

gcc main.c -L. -laaa_c_connector -o c_aaa 

tôi sẽ cần phải thiết lập LD_LIBRARY_PATH chứa $ PWD và nếu tôi chạy tệp thực thi ./c_aaa, tôi sẽ nhận được kết quả mong đợi:

Hi David 
Hi James 

EDIT:

Trên một số bản phân phối Linux, -laaa-lstdc++ cũng có thể được yêu cầu cho lệnh biên dịch cuối cùng. Nhờ @AlaaM. cho sự chú ý

+1

Cảm ơn bạn rất nhiều. Câu trả lời chi tiết của bạn đã giúp tôi rất nhiều. – Abhinav

+1

bạn cũng có thể chỉ định đường dẫn liên kết lib với 'gcc usecpp.c -L. -laaa_c_connector -Wl, -rpath ,. -o c_aaa' – Metaphox

+0

'usecpp.c' là gì? –