Sử dụng thư viện C ++ trong mã C


102

Tôi có một thư viện C ++ cung cấp nhiều lớp khác nhau để quản lý dữ liệu. Tôi có mã nguồn cho thư viện.

Tôi muốn mở rộng API C ++ để hỗ trợ các lệnh gọi hàm C để thư viện có thể được sử dụng với mã C và mã C ++ cùng một lúc.

Tôi đang sử dụng chuỗi công cụ GNU (gcc, glibc, v.v.), vì vậy hỗ trợ ngôn ngữ và kiến ​​trúc không phải là vấn đề.

Có bất kỳ lý do tại sao điều này là không thể về mặt kỹ thuật ?

Có bất kỳ của Gotcha mà tôi cần phải xem ra cho?

Có tài nguyên, mã ví dụ và / hoặc tài liệu về điều này không?


Một số điều khác mà tôi đã tìm ra:

  1. Sử dụng phần sau để bao bọc các tiêu đề C ++ của bạn cần được mã C sử dụng.

#ifdef __cplusplus
extern "C" {  
#endif  
//  
// Code goes here ...  
//  
#ifdef __cplusplus  
} // extern "C"  
#endif
  1. Giữ các giao diện C ++ "thực" trong các tệp tiêu đề riêng biệt mà C. Hãy suy nghĩ nguyên tắc PIMPL ở đây. Sử dụng #ifndef __cplusplus #errorcông cụ giúp ở đây để phát hiện bất kỳ sự điên rồ nào.
  2. Cẩn thận với các định danh C ++ làm tên trong mã C
  3. Enums có kích thước khác nhau giữa các trình biên dịch C và C ++. Có lẽ không phải là vấn đề nếu bạn đang sử dụng chuỗi công cụ GNU, nhưng hãy cẩn thận.
  4. Đối với các cấu trúc theo mẫu sau để C không bị nhầm lẫn.

    typedef struct X { ... } X
  5. Sau đó, sử dụng con trỏ để truyền xung quanh các đối tượng C ++, chúng chỉ cần được khai báo trong C là struct X trong đó X là đối tượng C ++.

Tất cả những điều này là nhờ sự hỗ trợ của một người bạn là thuật sĩ về C ++.


5
Hơi muộn, nhưng tôi đã viết một hướng dẫn nhỏ về trình bao bọc C cho C ++: teddy.ch/c++_library_in_c
Teddy

Câu trả lời:


69

Vâng, điều này chắc chắn là có thể. Bạn sẽ cần viết một lớp giao diện bằng C ++ khai báo các hàm với extern "C":

extern "C" int foo(char *bar)
{
    return realFoo(std::string(bar));
}

Sau đó, bạn sẽ gọi foo()từ mô-đun C của mình, mô-đun này sẽ chuyển lệnh gọi tới realFoo()hàm được triển khai trong C ++.

Nếu bạn cần hiển thị một lớp C ++ đầy đủ với các thành viên và phương thức dữ liệu, thì bạn có thể cần thực hiện nhiều công việc hơn ví dụ hàm đơn giản này.


Chỉ nên extern "C"đặt trong các khai báo (và không phải trong định nghĩa)? Vì bạn đã đề cập đến "lớp khai báo các hàm" nhưng mã mẫu của bạn cũng là một định nghĩa. Nói cách khác, chúng ta nên đặt nó trong các tập tin tiêu đề hoặc các tập tin nguồn (hoặc cả hai?)?
kyriakosSt

@KyrSt: Nếu bạn có tệp tiêu đề có khai báo hàm, thì ít nhất bạn phải đặt extern "C"ở đó. Trình biên dịch của bạn sẽ cho bạn biết nếu bạn cũng phải đặt nó vào định nghĩa.
Greg Hewgill

23

C ++ FAQ Lite: "Cách trộn mã C và C ++" .

Một số vấn đề được mô tả trong câu trả lời cho những câu hỏi sau:

  • [32.8] Làm cách nào để truyền một đối tượng của lớp C ++ đến / từ một hàm C?
  • [32.9] Hàm C của tôi có thể truy cập trực tiếp dữ liệu trong một đối tượng của lớp C ++ không?

12

Chính gotcha: không thể bắt các ngoại lệ trong C. Nếu có khả năng xảy ra ngoại lệ trong mã C ++, hãy viết mã C hoặc trình bao bọc C ++ của bạn thật cẩn thận. Ngược lại, các cơ chế ngoại lệ như (ví dụ: longjump) trong mã C (như được tìm thấy trong các ngôn ngữ kịch bản khác nhau) không bắt buộc phải gọi hàm hủy cho các đối tượng C ++ trên ngăn xếp.


2
Điểm tuyệt vời về cuộc gọi longjump. Mặc dù tôi không sử dụng chúng trực tiếp, nhưng các khuôn khổ thử nghiệm mà tôi sử dụng sẽ triển khai chúng. Một cái gì đó để giữ trong tâm trí. Cảm ơn
Misha M

3

bạn có thể trộn mã C / C ++. Nếu hàm main () của bạn trong C ++, thì bạn chỉ cần đảm bảo rằng các hàm c của bạn được khai báo

extern "C"

Nếu chính của bạn là C, thì bạn có thể OK ngoại trừ các biến tĩnh. Bất kỳ hàm tạo nào với các biến tĩnh của bạn phải được gọi trước khi bắt đầu hàm main (). Điều này sẽ không xảy ra nếu C là chính của bạn. Tôi bạn có rất nhiều biến static, điều tốt nhất cần làm là thay thế các biến static bằng các singleton.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.