Gọi qua lại giữa code C và C++

Image

Chúng ta vẫn biết rằng C++ là “C with Classes”, vẫn thường xuyên viết code trộn giữa C++ và C. Trong C++ chúng ta gọi C dễ dàng, ví dụ như hàm printf của stdio.h được gọi trong code C++ bình thường. Tuy nhiên, ở chiều ngược lại, trong C mà muốn gọi C++ thì đòi hỏi phải nắm được một số quy tắc.

Trước hết cần định nghĩa thế nào là hàm C, thế nào là hàm C++. Xét ví dụ hàm test() với implementation của nó mà không có lỗi khi compile:

void test() {…}

  • Nếu lấy compiler C (ví dụ: gcc) đem đi compile hàm test() thì hiển nhiên test() là hàm C.
  • Nếu lấy compiler C++ (ví dụ: g++) đem đi compile hàm test() thì sẽ có 3 trường hợp:
    • Nếu prototype của test() được khai báo là: extern “C” void test(); thì test() là hàm C.
    • Nếu implementation của test() được khai báo là: extern “C” void test() {…} thì test() là hàm C.
    • Nếu không phải là một trong 2 trường hợp trên thì test() là hàm C++.

Xong phần định nghĩa, bây giờ xem xét cách gọi hàm C bên trong C++, ví dụ:

// File main.cpp, code C++
void hehe() { printf(“hello”); }

Nếu chỉ có như vậy, compiler sẽ báo lỗi: “printf là chi vậy cha nội?”.

Nguyên tắc 1: để có thể gọi được một hàm (bất kể trong C hay C++) thì prototype hoặc implementation của hàm đó luôn phải được khai báo ở phía trên nơi gọi.

Ta sửa lại bằng cách thêm prototype của printf :

// File main.cpp, code C++
int printf(const char *, …);
void hehe() { printf(“hello”); }

Compile file main.cpp thì OK, nhưng lúc link thì linker sẽ báo lỗi không tìm thấy implementation của printf, sao lạ vậy?

Nguyên tắc 2: khi gọi một hàm trong C++ thì khai báo prototype của hàm đó quyết định nó là hàm C hay C++.

Với prototype như trên, printf được coi là hàm C++, và hiển nhiên không hề có implementation C++ của printf. Ta viết lại như sau thì mới OK hết:

// File main.cpp, code C++
extern “C” int printf(const char *, …);
void hehe() { printf(“hello”); }

Nếu ta mở file stdio.h (của các loại thư viện Win32, Linux, Android NDK, Bada, …) ra xem thì thấy hàm printf có prototype luôn luôn được khai báo là extern “C”, do đó nếu viết như sau thì cũng OK (đây là cách mà ta thường xuyên viết theo thói quen):

// File main.cpp, code C++
#include <stdio.h>
void hehe() { printf(“hello”); }

Cuối cùng là cách gọi hàm C++ bên trong code C.

Nguyên tắc 3: trong code C chỉ có thể gọi hàm C mà thôi.

Ví dụ:

// File main.c, code C
void test();
void hihi() { test(); }

// File test.cpp, code C++
void test() {}

Để hihi() gọi được test() thì bắt buộc test() phải là hàm C, do vậy ta thêm khai báo extern “C” cho prototype của test():

// File test.cpp, code C++
extern “C” void test();
void test() {}

Chúng ta có thể thấy chỉ riêng chuyện gọi qua lại C và C++cũng là một nguyên nhân khiến hai ngôn ngữ này được coi là những ngôn ngữ phức tạp.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s