2010-02-04 43 views
14

Tôi đang cố truy cập lớp C++ và gọi phương thức của nó từ tệp .c.Cách gọi một lớp C++ và phương thức của nó từ một tệp c

Tôi google chủ đề này và tìm thấy điều này http://developers.sun.com/solaris/articles/mixing.html

Nó nói:

You can write extern "C" functions in C++ that access class M objects and call them from C code.

Đây là một chức năng C++ được thiết kế để gọi hàm thành viên foo:

extern "C" int call_M_foo(M* m, int i) { return m->foo(i); } 

Câu hỏi của tôi là nơi tôi đặt dòng giới thiệu? Trong tệp C++ .h của tôi? Hoặc C .h tệp?

Và nó đi vào và nói:

Dưới đây là một ví dụ về mã C có sử dụng lớp M:

struct M;      // you can supply only an incomplete declaration 

int call_M_foo(struct M*, int); // declare the wrapper function 

int f(struct M* p, int j)  // now you can call M::foo 
{ 
    return call_M_foo(p, j); 
} 

Nhưng làm thế nào/nơi nào tôi có thể tạo lớp M trong tập tin C của tôi ? Và tôi đặt mã ở trên ở đâu? C .h tệp? C++ .h tệp? Hoặc C .c tệp?

Cảm ơn bạn.

Cảm ơn bạn về câu trả lời chi tiết của GMan. Tôi đã làm theo đề xuất của bạn. Nhưng tôi nhận được lỗi biên dịch trong tập tin .c của tôi.

main.c:33:
./some_class.h:24: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘attribute’ before ‘’ token
./some_class.h:25: error: expected ‘)’ before ‘
’ token
./some_class.h:26: error: expected ‘)’ before ‘*’ token

Và đây là some_class.h tôi dòng 24-26:

#ifdef __cplusplus 
class M { 

public: 
    M(); 
    virtual ~M(); 

    void method1(char* name, char* msg); 
}; 

extern "C" { 
#else 
struct M; 
#endif 

    /* access functions line 24-26 are here*/ 
    M* M_new(void); 
    void M_delete(M*); 
    void M_method1(M*, char*, char*); 
#ifdef __cplusplus 
} 
#endif 

Đối với một số lý do, trình biên dịch C của tôi không thích extern "C" trong GMAN 's gốc some_test.h. Vì vậy, tôi phải sửa đổi ở trên. Dường như trình biên dịch C không thích/hiểu dòng struct M;.

Bất kỳ ý tưởng nào cũng sẽ được đánh giá cao.

+0

Trong khi tôi không nói về extern "C", có rất nhiều thông tin liên quan trong bài đăng này về cách truy cập OO C++ apis từ C http://stackoverflow.com/questions/2045774/developing-c-wrapper-api -for-object-oriented-c-code/ –

+0

@Peter: Tôi đã sửa chữa công cụ "C" bên ngoài. Trình biên dịch C không biết ý nghĩa của nó, vì vậy chúng ta tạo một macro biến thành 'extern" C "' trong C++ và không có gì trong C. Lý do chúng ta cần extern "C" là vì C++ mangles tên khi biên dịch. C không, do đó, bằng cách sử dụng 'extern" C "' chúng tôi cho trình biên dịch biết để cho điều này được biên dịch "C cách". – GManNickG

Trả lời

25

tập tin của bạn tiêu đề, được chia sẻ giữa C và C++:

#ifdef __cplusplus // only actually define the class if this is C++ 

class some_class 
{ 
    public: 
     int some_method(float); 
}; 

#else 

// C doesn't know about classes, just say it's a struct 
typedef struct some_class some_class; 

#endif 

// access functions 
#ifdef __cplusplus 
    #define EXPORT_C extern "C" 
#else 
    #define EXPORT_C 
#endif 

EXPORT_C some_class* some_class_new(void); 
EXPORT_C void some_class_delete(some_class*); 
EXPORT_C int some_class_some_method(some_class*, float); 

Sau đó bạn nguồn file:

#include "some_foo.h" 

int some_class::some_method(float f) 
{ 
    return static_cast<int>(f); 
} 

// access functions 
EXPORT_C some_class* some_class_new(void) 
{ 
    return new some_class(); 
} 

EXPORT_C void some_class_delete(some_class* this) 
{ 
    delete this; 
} 

EXPORT_C int some_class_some_method(some_class* this, float f) 
{ 
    return this->some_method(f); 
} 

Bây giờ biên dịch mã nguồn đó, và liên kết với nó. Nguồn C của bạn sẽ giống như sau:

#include "some_class.h" 

some_class* myInstance = some_class_new(); 

int i = some_class_some_method(myInstance, 10.0f); 

some_class_delete(myInstance); 

Nếu bạn nghiêm túc về việc trộn C và C++, bạn sẽ muốn macro.


Dưới đây là một số mẫu vĩ mô của mà có thể làm này dễ dàng hơn nhiều:

// in something like c_export.h 
// extern "C" macro 
#ifdef __cplusplus 
    #define EXPORT_C extern "C" 
#else 
    #define EXPORT_C 
#endif 

// new 
#define EXPORT_C_CLASS_NEW(classname) EXPORT_C \ 
      classname * classname##_new(void) 

#define EXPORT_C_CLASS_NEW_DEFINE(classname) \ 
      EXPORT_C_CLASS_NEW(classname) \ 
      { return new classname(); } 

// repeat as much as you want. allows passing parameters to the constructor 
#define EXPORT_C_CLASS_NEW_1(classname, param1) EXPORT_C \ 
      classname * classname##_new(param1 p1) 

#define EXPORT_C_CLASS_NEW_1_DEFINE(classname, param1) \ 
      EXPORT_C_CLASS_NEW_1(classname, param1) \ 
      { return new classname (p1); } 

// delete 
#define EXPORT_C_CLASS_DELETE(classname) EXPORT_C \ 
      void classname##_delete(classname * this) 

#define EXPORT_C_CLASS_DELETE_DEFINE(classname) \ 
      EXPORT_C_CLASS_DELETE(classname) \ 
      { delete this; } 

// functions 
#define EXPORT_C_CLASS_METHOD(classname, methodname, ret) EXPORT_C \ 
      ret classname##_##methodname##(classname * this) 

#define EXPORT_C_CLASS_METHOD_DEFINE(classname, methodname, ret) \ 
      EXPORT_C_CLASS_METHOD(classname, methodname, ret) \ 
      { return this->##methodname##(); } 

// and repeat as necessary. 
#define EXPORT_C_CLASS_METHOD_1(classname, methodname, ret, param1) EXPORT_C \ 
      ret classname##_##methodname(classname * this, param1 p1) 

#define EXPORT_C_CLASS_METHOD_1_DEFINE(classname, methodname, ret, param1) \ 
      EXPORT_C_CLASS_METHOD_1(classname, methodname, ret, param1) \ 
      { return this->##methodname##(p1); } 

Và vân vân. Tiêu đề/nguồn của chúng tôi trở thành:

// header 
#include "c_export.h" // utility macros 

#ifdef __cplusplus // only actually define the class if this is C++ 

class some_class 
{ 
    public: 
     int some_method(float); 
}; 

#else 

// C doesn't know about classes, just say it's a struct 
typedef struct some_class some_class; 

#endif 

// access functions 
EXPORT_C_CLASS_NEW(some_class); 
EXPORT_C_CLASS_DELETE(some_class); 
EXPORT_C_CLASS_METHOD_1(some_class, some_method, int, float); 

// source 
#include "some_foo.h" 

int some_class::some_method(float f) 
{ 
    return static_cast<int>(f); 
} 

// access functions 
EXPORT_C_CLASS_NEW_DEFINE(some_class); 
EXPORT_C_CLASS_DELETE_DEFINE(some_class); 
EXPORT_C_CLASS_METHOD_1_DEFINE(some_class, some_method, int, float); 

Và điều đó ngắn gọn hơn nhiều. Nó có thể được thực hiện đơn giản hơn (có thể) với vĩ mô variadic, nhưng đó là phi tiêu chuẩn và tôi để nó cho bạn. :] Ngoài ra, bạn có thể tạo macro cho các hàm không phải thành viên thông thường.


Lưu ý rằng C không không biết tham chiếu là gì. Nếu bạn muốn liên kết với một tham chiếu, đặt cược tốt nhất của bạn có thể chỉ để viết định nghĩa xuất khẩu theo cách thủ công. (Nhưng tôi sẽ suy nghĩ về nó, có lẽ chúng ta có thể tự động lấy nó).

Hãy tưởng tượng some_class của chúng tôi đã tham chiếu float bởi (không phải const) (vì bất kỳ lý do gì). Chúng tôi sẽ xác định chức năng như sau:

// header 
// pass by pointer!          v 
EXPORT_C_CLASS_METHOD_1(some_class, some_method, int, float*) ; 

// source 
EXPORT_C_CLASS_METHOD_1(some_class, some_method, int, float*) 
{ 
    // dereference pointer; now can be used as reference 
    return this->some_method(*p1); 
} 

Và ở đó chúng tôi đi. C sẽ giao diện với các tham chiếu có con trỏ thay thế:

// c source, if some_method took a reference: 
float f = 10.0f; 
int i = some_class_some_method(myInstance, &f); 

Và chúng tôi vượt qua f "theo tham chiếu".

+0

Một nit - hàm some_class_construct/destruct của bạn nên được gọi là some_class_new/delete thay vì đó là những gì chúng làm. Gọi họ là xây dựng/phá hủy rất khó hiểu –

+0

@ Chris: Vâng, họ * làm * xây dựng và phá hủy, nhưng bạn nói đúng; đó không phải là mục đích chính của họ. – GManNickG

+0

@GMan - trong C, nếu bạn chỉ cần 'struct name', bạn không thể chỉ nó đơn giản là' name'. Để làm điều đó, bạn muốn làm 'typedef struct name name' –

3

Bạn cần phải phân chia nó giữa các tiêu đề C++ và tệp triển khai.

foo.h:

extern "C" int call_M_foo(M* m, int i); 

foo.cc:

extern "C" int call_M_foo(M* m, int i) { 
    return m->foo(i); 
} 

Để tạo đối tượng của loại M, bạn sẽ cần một chức năng tương tự:

foo.h:

struct M; 
extern "C" M* create_M(); 

foo.cc:

extern "C" M* create_M() { 
    return new M; 
} 
0

Trang web mà bạn đã liên kết để có câu trả lời đã:

You can declare function print in a header file that is shared by C and C++ code:

#ifdef __cplusplus extern "C" 
#endif int print(int i, double d); 

You can declare at most one function of an overloaded set as extern "C" Here is the example C header for the wrapper functions:

int g_int(int); 
double g_double(double); 

Về cơ bản, có thể có một tiêu đề chia sẻ giữa hai rằng tuyên bố chức năng nguyên mẫu, thêm các sửa đổi "C" bên ngoài nếu bạn đang ở trong C + + để đảm bảo chức năng có thể được truy cập trong một đối tượng từ C. Bạn xác định cơ thể của functio n sau này trong mã C++ như bình thường, nếu cần thiết bên trong một lớp vv, và bạn sử dụng hàm trong C như bình thường.

2

Bạn có một số câu hỏi được kết hợp ở đây nên tôi sẽ trả lời riêng từng câu hỏi.

My question is where do I put the about line? In my c++ .h file? or c .h file?

Dòng extern "C" có trong tệp C++.Về cơ bản nó cho trình biên dịch biết giới hạn mọi thứ trong khối extern "C" tới tập con C của C++ và đến các hàm xuất được khai báo trong khu vực này cho phù hợp.

But how/where do I create the class M in my c file?

Bạn không thể. C không có khái niệm về các lớp học và hoàn toàn không có cách nào để trực tiếp tạo lớp học một cách trực tiếp. Về cơ bản, bạn phải xuất một hàm C trong tệp C++ của bạn để tạo lớp và trả về nó làm con trỏ. Sau đó, bạn có thể chuyển con trỏ đó quanh ứng dụng C của bạn. Bạn không thể sửa đổi trực tiếp lớp trong ứng dụng C của mình, vì C không hỗ trợ các lớp học và trình biên dịch C++ của bạn có thể chèn các biến "ẩn" cho sổ sách kế toán bên trong tờ khai thực tế của lớp học .

And where do I put the above code?

Đoạn mã sử dụng con trỏ ure struct nằm trong tệp C. Bạn đang sử dụng buộc phải sử dụng con trỏ ure struct vì C không hỗ trợ các lớp học. Bạn có thể thực hiện các cuộc gọi chức năng bằng cách sử dụng chức năng đó ở bất kỳ đâu trong tệp thực thi C , giống như các cuộc gọi hàm C bình thường.

1

Tất cả thông tin bạn cần có trong liên kết bạn cung cấp. Bạn chỉ cần hiểu rằng cần phải có sự tách biệt chặt chẽ giữa mã C và C++.

  1. Mã C++ có thể gọi bất kỳ mã C nào.
  2. Mã C thường là không thể gọi bất kỳ mã C++ nào.
  3. Chức năng C có thể được triển khai bằng mã C++.

Điều quan trọng cần hiểu là trình biên dịch C và C++ mangle tên hàm khi tạo tệp đối tượng theo nhiều cách khác nhau, vì vậy chúng thường không thể tương tác (tại thời gian liên kết), ngoại trừ C++ biết sự khác biệt bằng cách sử dụng extern "C"

Nguyên mẫu: void f(int); có thể được đọc sai bởi một trình biên dịch C đến: _f, nhưng C++ biên dịch có thể chọn một tên ví dụ rất khác nhau f_int, và do đó mối liên kết sẽ không biết họ có nghĩa vụ là như nhau.

Tuy nhiên:

extern "C" void f(int);

sẽ được đọc sai bởi một ++ biên dịch C đến _f, nhưng một trình biên dịch C sẽ nghẹt thở trên extern "C". Để tránh điều này, bạn nên sử dụng một cái gì đó như thế này:

#ifdef __cplusplus 
extern "C" { 
#endif 

void f(int); 

#ifdef __cplusplus 
} /* closing brace for extern "C" */ 
#endif 

Bây giờ toàn bộ phần trên có thể sống trong một file .h và là, như các sun.com bang bài viết, một tiêu đề hỗn hợp ngôn ngữ.

Điều này có nghĩa rằng một .c hoặc cpp tập tin có thể #include tiêu đề này và mã có thể gọi f();
hoặc là một .c hay.tập tin cpp thể #include tiêu đề này và thực hiện nó:

void f() 
{ 
} 

Bây giờ các bit tốt là một tập tin cpp có thể thực hiện điều này để gọi bất kỳ C++ nó thích.

Bây giờ để trả lời câu hỏi cụ thể của bạn:

  1. Mẫu mã đầu tiên chỉ có thể đi trong một file cpp.
  2. Mẫu mã thứ hai chỉ có thể đi trong tệp .c.

Ngoài ra lớp M phải được khai báo và xác định trong tệp C++.

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