12 Mart 2019 Salı

extern "C" - C kodunun C++ İle Kullanılabilmesini Sağlar

Giriş
C kodunun C++ ile kullanılabilmesini sağlar. Açıklaması şöyleName Mangling yazısına da bakılabilir. 
Name mangling is not desirable when linking C modules with libraries or object files compiled with a C++ compiler. To prevent the C++ compiler from mangling the name of a function, you can apply the extern "C" linkage specifier to the declaration or declarations, as shown in the following example:
extern "C" {
   int f1(int);
   int f2(int);
   int f3(int);
};

This declaration tells the compiler that references to the functions f1, f2, and f3 should not be mangled.

The extern "C" linkage specifier can also be used to prevent mangling of functions that are defined in C++ so that they can be called from C. For example,
extern "C" {
   void p(int){
      /* not mangled */
   }
};

Genel Kullanım

Örnek
Şöyle yaparız.
 #ifdef __cplusplus
 extern "C" {
 #endif

 //// most of the header material goes here, C style

 #ifdef __cplusplus
 }; // end extern "C"
 #endif
Örnek
Şöyle yaparız.
#ifdef __cplusplus
extern "C" {
#endif

  // Header content

#ifdef __cplusplus
}
#endif

Örnek - C++ Kodunun DLL İle Dışa Açılması
DLL .h dosyasında şöyle yaparız.
#ifndef ADAPTER_H
#define ADAPTER_H
#endif

#ifdef __cplusplus
extern "C" {
#endif

  void adapter_Foo(int arg1, int *arg2);
  // more wrapped functions

#ifdef __cplusplus
}
#endif

#endif
DLL cpp dosyasında şöyle yaparız.
#include "adapter.h"
// includes for your C++ library here

void adapter_Foo(int arg1, int *arg2)
{
  // call your C++ function, e.g.
  ...
}
Örnek - C++ Class
C++ sınıfını C'ye geçmek için void* şeklinde tanımlama yapılıyor. Bu tehlikeli olabilir.
typedef void* CMyClass;
Elimizde şöyle bir kod olsun
Fred.h
--------------------------------

#ifdef  __cplusplus
class Fred
{
  public:
  Fred(int x,int y);
  int doStuff(int p);
};
#endif

//
// C Interface.
typedef void*   CFred;

//
// Need an explicit constructor and destructor.
extern "C" CFred  newCFred(int x,int y);
extern "C" void   delCFred(CFred);

//
// Each public method. Takes an opaque reference to the object
// that was returned from the above constructor plus the methods parameters.
extern "C" int    doStuffCFred(CFred,int p);
Şöyle yaparız.
CFred.cpp
--------------------------------

// Functions implemented in a cpp file.
// But note that they were declared above as extern "C" this gives them
// C linkage and thus are available from a C lib.
CFred newCFred(int x,int y)
{
  return reinterpret_cast<void*>(new Fred(x,y));
}

void delCFred(CFred fred)
{
  delete reinterpret_cast<Fred*>(fred);
}

int doStuffCFred(CFred fred,int p)
{
  return reinterpret_cast<Fred*>(fred)->doStuff(p);
}
Örnek - std::string
cpp içinde'deki metodlar olsun. İlk metod std::string aldığı için C'den çağırılamaz. Köprü vazifesi gören ve char* kullanan ikinci metod bu yüzden lazım.
//foo.cpp

void fooCPP(std::string& str)
{
  ......
}

extern "C" void fooWrap(char const * cstr)
{
    std::string str(cstr);
    fooCPP(str);
}
Daha sonra C'den çağırmak için extern "C" şeklinde değil, sadece extern kelimesi ile C++ metodunun bir başka dosyada olduğu belirtiliyor. 

/*main.c:*/
extern void fooWrap(char const * cstr); 
/*No 'extern "C"' here, this concept doesn't exist in C*/

void fooC()
{
    char const* str = "hello";
    fooWrap(str);
}
Karıştırılan bir noktaya değinmek gerekir. extern "C" anahtar kelimelerini gören C++ derleyicisi, C moduna geçmez. Zaten böyle bir mod da yoktur. extern "C" içindeki kod halen C++ derleyicisi tarafından derlenen C++ kodudur. Örnekte metod içinde std::string bu yüzden kullanılabiliyor.

Örnek - Yanlış kullanım
Problemin kaynağı main dosyasının C derleyicisi ile derlenmesi.

Elimizdeki .cpp dosyasını derleyelim.
g++ -o mylib.o -c mylib.cpp
Daha sonra C derleyicisini kullanarak linkleyelim.
gcc main.c mylib.o
C++ ile ilgili bir sürü unresolved ... hatası alırız. Çünkü C derleyici, C++ runtime kullanmaz.

Doğru kullanım için C derleyicisi ile dosyayı derleyelim
gcc -c cfile.c
Sonra C++ derleyicisi ile linkleriz.
g++ -o a.out main.o cfile.o mylib.o

Hiç yorum yok:

Yorum Gönder