Thứ Hai, 31 tháng 1, 2011

Trộn code C và C++ (Call C++ function from C code and vice versa/ Mixing C and C++)

Làm sao để có thể gọi một hàm C++ từ chương trình C và ngược lại??

  • Vấn đề: Bạn mất khá nhiều thời gian để vật lộn với đống code C (framework mà công ty hoặc lab đang sử dụng chẳng hạn). Thế rồi một ngày đẹp trời, thằng khách hàng đòi thêm chức năng mới và phải sử dụng đến thư viện mã nguồn từ bên ngoài… Nhưng than ôi, nó viết bằng C++, class tè le, try catch tùm lum, cout cin tanh bành. Hà hà, giờ thì bạn có 2 lựa chọn:
    • Một : chuyển toàn bộ code của cái thư viện chết tiệt đó thành mã C (nghĩ tới đã thấy nản :( );
    • Hai: Trộn hai thằng lại với nhau, cho tụi nó sống chung.
  • Lời khuyên: Chỉ nên trộn code kiểu này nếu không còn cách nào khác :))

  • Những điểm cần lưu ý:
    • Phải dùng trình biên dịch C++ để liên kết các tập tin object
    • Trình biên dich C và C++ phải tương thích với nhau (cùng phiên bản, cùng nhà phát triển)

  • Từ khóa:
1:  #ifdef __cplusplus  
2:  extern "C" {  
3:  #endif  
4:  //Khai báo hàm ở đây  
5:  #ifdef __cplusplus  
6:  }  
7:  #endif  

Bọc các khai báo hàm trong header file bằng keyword extern “C” {/*…*/}. Điều này có ý nghĩa như sau: “Ê, trình biên dịch C++, mấy cái hàm này là hàm C nha mày” . Sau quá trình biên dich, quá trình liên kết các tập tin object sẽ tìm kiếm symbol của các hàm được sử dụng trong mã nguồn. Khi đó, nếu ta không bọc các khai báo hàm như trên thì trình liên kiết (linker) sẽ tìm kiếm hàm C++ chứ không phải hàm C (mặc dù đã include tập tin header).

  • Mã nguồn tham khảo:
Môi trường:
Trình biên dịch: GNU C và GNU C++
OS: Fedora 13

Gọi hàm C++ từ chương trình C

Tập tin: header_c.h
1:  #ifdef __cplusplus  
2:  extern "C" {  
3:  #endif  
4:       void cpp_function( void );  
5:  #ifdef __cplusplus  
6:  }  
7:  #endif  

Tập tin: header_cpp.h
1:  //cpp_file.h  
2:  #include <iostream>  
3:  using namespace std;  
4:  #include "header_c.h"  
5:  class foo  
6:  {  
7:       public:  
8:            int i;  
9:            foo() {i=0; }   
10:           ~foo() {}  
11:           void see()  
12:           {  
13:               cout << "I am inside class" << endl;  
14:           }  
15: };  

Tập tin: foo.cpp
1:  //c_file.h  
2:  #include "header_cc.h"  
3:  //C++ function  
4:  #include <iostream>  
5:  #include <exception>  
6:  void cpp_function( void )  
7:  {  
8:       foo f;  
9:       int i = 0;  
10:       int P;  
11:       f.see();  
12:       cout<<"Hi, I am in Cpp file and called by C file\n"<<flush;  
13:       cout<<"Test try catch\n"<<flush;  
14:       try{  
15:            if(i==0) throw “Division by zero”;  
16:            else P = 5/i;  
17:       }  
18:       catch(const char* str){  
19:            cout << str << endl;  
20:       }  
21:  }  

Tập tin: main.c
1:  //C function (main)  
2:  #include <stdio.h>  
3:  #include <stdlib.h>  
4:  #include "header_c.h"  
5:  int main( void )  
6:  {  
7:       cpp_function();  
8:       return 0;  
9:  }  

Tập tin: Makefile (biên dịch)
Trong trường hợp này, tôi biên dịch hàm C++ thành static library
1:  #Use c++ function as static library  
2:  all:  
3:       g++ -Wall -c foo.cc  
4:       ar crv libfoo.a foo.o  
5:       ranlib libfoo.a  
6:       gcc -Wall -c main.c  
7:       g++ -Wall -o hic.exe main.o libfoo.a  
8:  clean:  
9:       rm -f *.o *.a hic.exe  
Kết quả thực thi:



Gọi hàm C từ chương trình C++
Tập tin: cfunc.h
1:  #ifdef __cplusplus  
2:  extern "C" {  
3:  #endif  
4:  void printHello();  
5:  #ifdef __cplusplus  
6:  }  
7:  #endif  

Tập tin: cfunc.c
1:  #include <stdio.h>  
2:  #include "gpp.h"  
3:  void printHello()  
4:  {  
5:       printf("I'm C function!\n");  
6:  }  

Tập tin: main.cpp
1:  #include <iostream>  
2:  using namespace std;  
3:  #include <gpp.h>  
4:  int main()  
5:  {  
6:       cout<<"This is c++ program, I will call a C function\n";  
7:       printHello();  
8:       return 0;  
9:  }  

Tập tin: Makefile
1:  all:  
2:       gcc -c gpp.c  
3:       ar crv libgpp.a gpp.o  
4:       ranlib libgpp.a  
5:       g++ -c main.c -I.  
6:       g++ -o main.out main.o libgpp.a  
7:  clean:  
8:       rm -f *.o *.a main.out  

Không có nhận xét nào:

Đăng nhận xét