2013-07-01 18 views
10

Tôi muốn một cách nội tuyến để xác định nguyên mẫu nào nên được bao gồm với C++. Ví dụ:Có cách nào để kết hợp các nguyên mẫu c và C++ không?

 
     void ArrayList_insert(ArrayList *arrlst, void *data, int i); 
IS_CPP void ArrayList_insert(ArrayList *arrlst, char *data, int i); 
IS_CPP void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i); 

Hiện tại tôi đang thực hiện:

 
#ifdef __cplusplus 
extern "C" { 
#endif 

....C HEADERS.. 

#ifdef __cplusplus 
} 

....C++ HEADERS... 

#endif 

nhưng nó rất bất tiện vì quá tải của các chức năng tương tự là ở những nơi khác nhau. Tôi chỉ có thể có 2 tập tin tiêu đề khác nhau, nhưng đó cũng là một nỗi đau. Do đó, Im tìm kiếm một giải pháp nội dòng như tôi đã đề xuất ở trên. Có ai biết cách để làm điều đó không?

Trả lời

14

Dễ dàng hơn bạn nghĩ.

#ifdef __cplusplus 
#define IS_C(x) extern "C" x ; 
#define IS_CPP(x) x ; 
#else 
#define IS_C(x) x ; 
#define IS_CPP(x) 
#endif 

Với kiểu này tiêu đề:

IS_C (void ArrayList_insert(ArrayList *arrlst, void *data, int i)) 
IS_CPP (void ArrayList_insert(ArrayList *arrlst, char *data, int i)) 
IS_CPP (void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i)) 
+0

điều này sẽ gây ra lỗi khi biên dịch với c vì c không hỗ trợ chức năng nạp chồng quá tải. – chacham15

+0

chacham15 - bạn nói đúng. Tôi đã sửa câu trả lời, vì tôi đã đọc sai câu hỏi. –

+0

Bây giờ nó tương đương với câu trả lời của @Carl Norum. – chacham15

11

Chắc chắn, bạn có thể làm điều đó gần giống như ví dụ của bạn bằng cách sử dụng một chức năng giống như vĩ mô:

#ifdef __cplusplus 
#define IS_CPP(x) x 
#else 
#define IS_CPP(x) 
#endif 

     void ArrayList_insert(ArrayList *arrlst, void *data, int i); 
IS_CPP(void ArrayList_insert(ArrayList *arrlst, char *data, int i)); 
IS_CPP(void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i)); 

Bây giờ nếu bạn biên dịch tiêu đề như C++, bạn sẽ nhận được tất cả ba, nhưng nếu bạn biên dịch như C, bạn sẽ chỉ nhận được một. Nếu bạn muốn chia sẻ một thư viện duy nhất giữa hai thư viện, bạn sẽ cần phải thêm một số phần tử số extern "C" vào hàm C khi biên dịch cho C++. Câu trả lời của @ MarkLakata cho thấy một cách có thể.

+0

+1 nhưng, có cách nào để thực hiện điều đó mà không có dấu đóng ngoặc đơn không? (im chỉ là một chút lập dị, sry) – chacham15

+0

Tôi không nghĩ vậy, không. –

+0

-1, điều này thiếu liên kết ngôn ngữ cần thiết. – Potatoswatter

1

Bạn rõ ràng có thể lạm dụng tiền xử lý để hack với nhau những gì bạn đang yêu cầu, nhưng tại sao làm điều đó? Cá nhân tôi muốn nhập mylst.insert(foop, 1); thay vì ArrayList_insert(mylst, foop, 1); nếu tôi đang sử dụng C++. Nói cách khác, tôi thấy ít lợi ích khi sử dụng kiểu C để gọi các hàm bị quá tải, nhưng trộn các kiểu của hàm gọi bạn là tác giả mã được tạo ra cũng không hẳn là chính xác.

Tôi muốn tạo một lớp ArrayList có cùng thành viên như trong cấu trúc C và bất cứ khi nào bạn cần giao diện lớp của bạn với hàm C, tạo bản sao nông nếu có thể và chuyển cho hàm C, sau đó sao chép thông tin từ cấu trúc đó trở lại lớp học của bạn.

Cách khác là bọc cấu trúc trong lớp C++ và sử dụng nó cho các chức năng giao diện C.

Nếu không, bạn có thể thử làm cho lớp kế thừa cấu trúc C, giả sử thẻ của cấu trúc không được đặt tên ArrayList và typedef cho cấu trúc không hiển thị từ giao diện C++. Sau đó, bạn hy vọng có thể truyền con trỏ này trực tiếp từ bên trong hàm thành viên như thể nó là cấu trúc C thực tế. Tôi không chắc phương pháp này là di động trong mọi trường hợp, vì vậy tôi sẽ thực hiện ý tưởng cũ nếu có thể, ngay cả khi nó yêu cầu sao chép dữ liệu qua lại.

Tất cả ý tưởng tránh trùng lặp mã và giao diện C++ trông giống mã C++ thay vì hỗn hợp xấu của hàm C và quá tải hàm C++. Ngoài ra, các giao diện được giữ riêng phần nào.Không tập tin tiêu đề bổ sung là cần thiết hoặc là các chức năng C có thể được bọc trong một extern "C" khối như thường lệ:

#ifdef __cplusplus 
extern "C" { 
#endif 

struct array_list_tag { 
    ... 
}; 

/* C functions here */ 

#ifdef __cplusplus 
} /* extern "C" */ 

class ArrayList ... 
#else /* !__cplusplus */ 
typedef struct array_list_tag ArrayList; 
#endif 
+0

"Tất cả ý tưởng tránh trùng lặp mã" Chính xác. Một trường hợp sử dụng ví dụ là nếu bạn đang viết một thư viện trong C++ có nghĩa là được tích hợp nhiều với C. Bạn tự mình gọi các hàm bị quá tải, nhưng cần phải có cách để C bạn tích hợp với để gọi chức năng là tốt.Các cấu trúc bao gói trong các lớp hoặc ngược lại làm tăng lượng trùng lặp mã và thêm một mâu thuẫn trong kiểu mã hóa trên nền tảng – chacham15

+0

Vì vậy, bạn đang nói rằng C có thể gọi mọi hàm bị quá tải? Sau đó thay vì ArrayList_insert cho C chỉ như OP được yêu cầu, nó bây giờ là ArrayList_insertBuffer, ArrayList_insertString, ArrayList_insertRawData, v.v ...? Vâng, giao diện C++ sẽ phù hợp với ít nhất ... Đối với các cấu trúc gói trong các lớp, có lẽ bạn đã đúng. Tôi chắc rằng có một cái gì đó trong tiêu chuẩn C++ yêu cầu một hàm có liên kết C để có thể truy cập dữ liệu lớp C++, miễn là không có thừa kế, hàm ảo và tất cả các cảnh báo khác đi kèm với các lớp trộn và C. –

3

Cách tiếp cận thông thường là chỉ cần viết nó theo cách rõ ràng nhất:

void ArrayList_insert(ArrayList *arrlst, void *data, int i); 
#ifdef __cplusplus 
void ArrayList_insert(ArrayList *arrlst, char *data, int i); 
void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i); 
#endif /* __cplusplus */ 

Như @ chacham15 chỉ ra, bạn cũng cần, trong một tiêu đề dự án rộng,

#ifdef __cplusplus 
#define EXTERN_C extern "C" 
#endif /* __cplusplus */ 

và bạn cần phải trang trí chức năng C-callable với EXTERN_C.

+0

nó không đơn giản, bạn đang thiếu '#ifdef __cplusplus extern" c "# endif' khai báo cho hàm c mà IMO làm cho nó trông thực sự kludgy. – chacham15

+0

@ Chacham15: khi bạn nhận xét ở đâu đó, '#ifdef __cplusplus #define IS_C extern" C "#else #define IS_C# endif' hoặc tương tự (tôi đề xuất tên như" EXTERN_C ") có thể được thực hiện sau khi tiếp cận của Pete rất sạch sẽ ... đẹp hơn nhiều so với gói mọi dòng trong macro, trùng lặp '))', hoặc có dấu chấm phẩy bên trong macro hoặc thiếu trực quan từ mỗi dòng .... –

1

Nếu bạn thực sự muốn loại bỏ bản mẫu và bạn sẵn sàng sử dụng bộ tiền xử lý để làm điều đó, thì hãy tiếp tục và viết lên mẫu. Các mô hình chung bạn đã trông như thế

extern "C" { 
    void C_accessible_declaration(); // this is all C sees 
} 

void Cxx_accessible_declaration_1(int); 
void Cxx_accessible_declaration_1(long); 

Vì vậy, bạn có thể làm cho một macro,

#ifdef __cplusplus 
# define C_PORTABLE_FUNCTION_SET(C_DECLS, CXX_DECLS) \ 
     extern "C" { C_DECLS } \ 
     CXX_DECLS 
#else 
# define C_PORTABLE_FUNCTION_SET(C_DECLS, CXX_DECLS) \ 
     C_DECLS 
#endif 

này hoạt động vì một tuyên bố chức năng bình thường không thể chứa dấu phẩy không được bao bọc bởi dấu ngoặc đơn. Nếu bạn muốn nó làm việc với các khuôn mẫu (với các tham số mẫu được phân tách bằng dấu phẩy), thì bạn có thể các macro variadic, được hỗ trợ trong C99, C++ 11 và các trình biên dịch khác nhau trước các tiêu chuẩn đó làm phần mở rộng.

#ifdef __cplusplus 
# define C_PORTABLE_FUNCTION_SET(C_DECLS, ...) \ 
     extern "C" { C_DECLS } \ 
     __VA_ARGS__ 
#else 
# define C_PORTABLE_FUNCTION_SET(C_DECLS, ...) \ 
     C_DECLS 
#endif 

Bây giờ điều này hoạt động miễn là các khai báo C không chứa dấu phẩy thường, có nghĩa là bạn không nên khai báo nhiều đối tượng trong một khai báo. Tôi đã gọi nó là C_PORTABLE_FUNCTION_SET để nhấn mạnh nó chủ yếu an toàn để sử dụng với các khai báo hàm, nhưng lưu ý rằng bạn cần khai báo các đối tượng C-access trong phạm vi extern C. Các định nghĩa được chia sẻ struct không được bảo vệ chút nào; chúng được bảo vệ bởi khái niệm C++ POD, và không mang liên kết ngôn ngữ.

Cách sử dụng:

#ifdef __cplusplus 
template< typename T, typename U > 
class Buffer { // still use #ifdef for the general case 
    ... 
}; 
#endif 

C_PORTABLE_FUNCTION_SET (
     void ArrayList_insert(ArrayList *arrlst, void *data, int i); 
, /* C++ */ 
     void ArrayList_insert(ArrayList *arrlst, char *data, int i); 

     template< typename T, typename U > 
     void ArrayList_insert(ArrayList *arrlst, Buffer< T, U > &data, int i); 
) 

Tôi không nghĩ rằng tôi muốn làm điều này bản thân mình, nhưng có vẻ như đủ an toàn để trở thành thành ngữ.

Các vấn đề liên quan