c/c++互相調用

1 C++中調用C的接口

咱們在閱讀一些庫的代碼的時候, 常常看到有些函數被extern 「C」來修飾ios

1.1 extern 「C」引入C的庫代碼

以下所示c++

extern "C" void func();
  • 若是須要修飾的函數比較多, 則使用以下方式
#ifdef __cplusplus
extern "C"
{
#endif

/////////////////////
//  一段代碼
/////////////////////

#ifdef __cplusplus
}
#endif
  • 若是你不想理解這段代碼的意義, 那麼請你記住 : 在你的代碼不知道是被c調用仍是c++調用時, 請添加此段代碼.

下面詳細說明此段代碼的意義:
__cplusplusc++編譯器(如g++等)定義的宏, 若是是c++調用的話, extern "C"聲明會有效. 若是時c調用的話, 那麼, extern 「C」`聲明無效git

要明白爲什麼使用extern 「C」, 還得從cpp中對函數的重載處理開始提及.github

在c++中,爲了支持重載機制,在編譯生成的彙編碼中,要對函數的名字進行一些處理, 加入好比函數的返回類型等等. 而在C中, 只是簡單的函數名字而已, 不會加入其餘的信息.app

也就是說 : C++C在編譯後對產生的函數名字的處理是不同的. 而上面的代碼extern "C"目的就是主要實現C與C++的相互調用問題.函數

對於C++編譯器, 因爲__cplusplus宏被定義, 所以經過extern "C"來通知C++編譯器編譯後的代碼是按照C的obj文件格式編譯的,要鏈接的話按照C的命名規則去找.this

CC++對函數的處理方式是不一樣的. extern "C"是使C++可以調用C寫做的庫文件的一個手段, 若是要對編譯器提示使用C的方式來處理函數的話, 那麼就要使用extern "C"來講明.編碼

這種方法有兩種妙用spa

  1. 在C源代碼中使用extern 「C」這樣代碼及時添加到C++的項目工程中, 也能夠正常的被編譯和連接.net

  2. 多數狀況下咱們C的庫都是SDK(包括頭文件和lib包), 沒有源代碼, 那麼在咱們的C++代碼中使用extern 「C」就通知編譯器咱們引入了C庫的代碼

1.2 示例程序

下面咱們經過一個示例來看看C++中若是調用C的函數, 代碼在language/c/cpp/cpp_link_c

咱們在add.c中定義了一個add函數, 這個函數是C語言實現的函數接口

// add.c
#include <stdio.h>
#include <stdlib.h>


int add(const int a, const int b)
{
    return (a + b);
}

 咱們C++中在main函數調用C語言實現的add函數

//  main.cpp
#include <iostream>
using namespace std;



#ifdef __cplusplus
extern "C"
{
#endif

int add(const int a, const int b);

#ifdef __cplusplus
}
#endif



int main( )
{
    std::cout <<add(3, 4) <<std::endl;

    return 0;
}

下面是咱們的Makefile信息, 咱們生成了兩個可執行程序main_normal和main_sdk

  • main_normal爲main.o和add.o直接編譯生成, C++中經過extern 「C」直接以源碼的方式生成了main_normal

  • main_sdk相似於咱們開發的方式, 首先用add.c生成了一個sdk庫libadd.so, 而後main.c中經過extern 「C」以C的方式連接了libadd.so中的add函數, 生成了main_sdk

#  the compile options
CFLAGS = -Wall -std=gnu99 -O2 -pedantic -Wextra -g
CXXFLAGS = -Wall -std=c++11 -O2 -pedantic -Wextra -g

SHAREDLIB_LINK_OPTIONS = -shared


FPIC = -fPIC

#  the include directory
INC = -I./


target=main_sdk main_normal libadd.so



all:$(target)



main_sdk : main.o libadd.so
    $(CXX) $^ -o $@ -L./ -ladd


main_normal : main.o add.o
    $(CXX) $^ -O $@

libadd.so : add.o
    $(CC) $(SHAREDLIB_LINK_OPTIONS) $(FPIC) $(LDFLAGS) $^ -o $@

#libmyclass.a:myclass.o func.o
#   ar crv $@ $^


%.o : %.cpp
    $(CXX) $(FPIC) $(CXXFLAGS) -c $^ -o $@ $(INC)


%.o : %.c
    $(CC) $(FPIC) $(CFLAGS) -c $^ -o $@ $(INC)


clean :
    rm -rf *.o
    rm -rf $(target)

C++中調用C的函數

 

因爲C++是高度兼容C的, 只須要通知C++編譯器按照C的命名方式編譯和連接二進制代碼便可, 除了編譯命名的處理不須要額外的層次, 所以這種方式很好的解決了, C++代碼中編譯連接C源代碼的問題

可是C語言卻不支持C++面向對象的特性, 這個問題該怎麼解決啊.

C調用C++的函數接口信息

前面咱們講解了, C++是一個C基礎上擴展的支持面向對象的高級語言, 所以咱們將C調用C++的函數的方法分爲面向過程和麪向對象兩種特性分開討論.

  • C中調用C++中基本的數據和成員(面向過程的數據)

  • C中調用C++中類成員數據(面向對象的數據)

2 C中調用C++ 的接口

C++面向過程的部分是徹底兼容C的, 所以其本質上俊只是編譯階段的處理不一樣而已, 可是C++也引入了一些新的特性, 好比函數重載等, 這些須要咱們單獨去兼容.

2.1 C中調用C++數據和成員(面向過程的數據)

2.1.1 基本函數的處理

這部分C與C++是徹底兼容的, 沒有區別, 所以使用extern 「C」的方式就足以處理.

將C++函數聲明爲」extern 「C」(在你的C++代碼裏作這個聲明), 而後調用它(在你的C或者C++代碼裏調用).

例如:

咱們有add.cpp作出的一套C++的庫接口, 其中包含add函數接口, 可是這套接口是C++的, 咱們想要在C程序中使用這個C++的庫接口, 該如何實現呢

咱們一樣以一段示例來展現, 參見language/c/cpp/c_link_cpp_func

首先是咱們的C++庫的源代碼

// add.cpp
int add(const int a, const int b)
{
    return (a + b);
}

咱們想要在C程序中使用這個函數接口, 可是C++並不兼容C的接口, 考慮咱們能夠經過增長一箇中間層來實現, 進行一次封裝, 將C++的庫封裝成C編譯器可識別的形式

中間層libadd.cpp的形式以下, 其實就是用C++編譯器編譯出一套C編譯器可識別的代碼, 一樣是經過extern "C"來實現, 將add函數封裝成call_cpp_add函數

//  libadd.cpp
int add(const int a, const int b);

#ifdef __cplusplus
extern "C"
{
#endif

int call_cpp_add(const int a, const int b)
{
    return add(a, b);
}

#ifdef __cplusplus
}
#endif

 

那這樣以來call_cpp_add函數雖然用C++編譯器編譯, 可是編譯成C編譯器可識別的格式, 咱們就能夠在C源程序main中調用C編譯器能夠識別的call_cpp_add函數.

//  main.c
#include <stdio.h>
#include <stdlib.h>


int call_cpp_add(const int a, const int b);

int main( )
{
    printf("%d\n", call_cpp_add(2, 4));

    return 0;
}

下面是Makefile的信息

#  the compile options
CFLAGS = -Wall -std=gnu99 -O2 -pedantic -Wextra -g
CXXFLAGS = -Wall -std=c++11 -O2 -pedantic -Wextra -g

SHAREDLIB_LINK_OPTIONS = -shared


FPIC = -fPIC

#  the include directory
INC = -I./

target=main_sdk libadd.so


all:$(target)


main_sdk : main.o libadd.so
    $(CC) $^ -o $@ -L./ -ladd -lstdc++


libadd.so : libadd.o add.o
    $(CXX) $(SHAREDLIB_LINK_OPTIONS) $(FPIC) $(LDFLAGS) $^ -o $@


%.o : %.cpp
    $(CXX) $(FPIC) $(CXXFLAGS) -c $^ -o $@ $(INC)

%.o : %.c
    $(CC) $(FPIC) $(CFLAGS) -c $^ -o $@ $(INC)

clean :
    rm -rf *.o
    rm -rf $(target)

C中調用C++中基本的數據和成員(面向過程的數據)

2.1.2 C語言調用C++重載函數的處理

C++支持函數重載的, 函數名相同可是參數不一樣的重載函數在編譯後連接的名字並不相同而能夠被識別, 這種狀況下, 咱們引入一箇中間層的方法一樣能夠實現C中調用C++的函數接口, 其實現與上一節C中調用C++非重載基本函數成員的實現沒有什麼區別, 只是爲各個重載函數均實現一套接口而已

咱們仍然以一個示例來展現, 代碼參見language/c/cpp/c_link_cpp_overload_func

首先是咱們的C++接口, 以下所示

//  add.cpp
//#include <iostream>
int add(const int a, const int b)
{
    return (a + b);
}

double add(const double a, const double b)
{
    //std::cout <<a <<", " <<b <<std::endl;
    return (a + b);
}

咱們爲此實現一箇中間層libadd.cpp, 經過C++編譯器用extern "C"將其編譯成C編譯器可識別的接口

// libadd.cpp
int add(const int a, const int b);
double add(const double a, const double b);

#ifdef __cplusplus
extern "C"
{
#endif

int call_cpp_add_int(const int a, const int b)
{
    return add(a, b);
}

double call_cpp_add_double(const double a, const double b)
{
    return add(a, b);
}

#ifdef __cplusplus
}
#endif

 

最後是咱們的C源程序, 調用咱們的中間層

//  main.c
#include <stdio.h>
#include <stdlib.h>


int call_cpp_add_int(const int a, const int b);
double call_cpp_add_double(const double a, const double b);

int main( )
{
    printf("2 + 4 = %d\n", call_cpp_add_int(2, 4));
    printf("2.1 + 4.5 = %lf\n", call_cpp_add_double(2.1, 4.5));

    return 0;
}

最後是Makefile, 咱們經過中間層libadd.cppC++的接口轉換成C編譯器能夠識別的格式, 而後添加在咱們的C源程序main.c

#  the compile options
CFLAGS = -Wall -std=gnu99 -O2 -pedantic -Wextra -g
CXXFLAGS = -Wall -std=c++11 -O2 -pedantic -Wextra -g

SHAREDLIB_LINK_OPTIONS = -shared

FPIC = -fPIC

#  the include directory
INC = -I./

target=main_sdk libadd.so

all:$(target)


main_sdk : main.o libadd.so
    $(CC) $^ -o $@ -L./ -ladd -lstdc++

libadd.so : libadd.o add.o
    $(CXX) $(SHAREDLIB_LINK_OPTIONS) $(FPIC) $(LDFLAGS) $^ -o $@

%.o : %.cpp
    $(CXX) $(FPIC) $(CXXFLAGS) -c $^ -o $@ $(INC)

%.o : %.c
    $(CC) $(FPIC) $(CFLAGS) -c $^ -o $@ $(INC)

clean :
    rm -rf *.o
    rm -rf $(target)

<code>C</code>語言調用<code>C++</code>重載函數的處理

2.2 C中調用C++中類成員數據(面向對象的數據)

2.2.1 C調用C++中成員函數

一樣以一個示例來展現調用的過程, 代碼參見language/c/cpp/c_link_cpp_mem_func

首先是myclass類的信息

/////////////////////
// myclass.h
/////////////////////
#ifndef __MY_CLASS_H_INCLUDE__
#define __MY_CLASS_H_INCLUDE__


#include <iostream>

using namespace std;


class MyClass
{
public :
    //  member function
    int add(int a, int b);
};



#endif  //  #define __MY_CLASS_H_INCLUDE__


/////////////////////
//  myclass.cpp
/////////////////////
#include "myclass.h"


//  member function
int MyClass::add(int a, int b)
{
    return (a + b);
}

 

接着咱們實現的接口, call_cpp_class_add函數中建立了一個MyClass對象並調用了其成員函數add, 因爲extern "C"的做用C++編譯器將libmyclass編譯成了一個能夠被C編譯器連接的目標對象格式

/////////////////////
//  libmyclass.cpp
/////////////////////
#include <iostream>
using namespace std;

#include "myclass.h"


#ifdef __cplusplus
extern "C"
{
#endif


/* extern "C" */int call_cpp_class_add(int a, int b)
{
    MyClass mc;

    return mc.add(a, b);
}


#ifdef __cplusplus
}
#endif

 

而後是main函數

/////////////////////
//  main.cpp
/////////////////////
#include <stdio.h>
#include <stdlib.h>

extern int call_cpp_class_add(int a, int b);

int main(void)
{
    printf("2 + 4 = %d\n", call_cpp_class_add(2, 4));

    return 0;
}

最後附上咱們的Makefile, 靜態庫連接後生成了main, 而動態連接庫連接後生成了main_sdk

#####################
#  Makefile
#####################

#  the compile options
CFLAGS = -Wall -std=gnu99 -O2 -pedantic -Wextra -g
CXXFLAGS = -Wall -std=c++11 -O2 -pedantic -Wextra -g

SHAREDLIB_LINK_OPTIONS = -shared

FPIC = -fPIC
#  the include directory
INC = -I./


target=libmyclass.so libmyclass.a main main_sdk



all:$(target)


main : main.o libmyclass.a
    $(CC) $^ -o $@ -ldl -lstdc++


main_sdk : main.o libmyclass.so
    $(CC) $^ -o $@ -ldl -lstdc++ -L./ -lmyclass



libmyclass.a : myclass.o libmyclass.o
    ar crv $@ $^


libmyclass.so : libmyclass.o myclass.o
    $(CXX) $(SHAREDLIB_LINK_OPTIONS) $(FPIC) $(LDFLAGS) $^ -o $@


%.o:%.cpp
    $(CXX) $(FPIC) $(CXXFLAGS) -c $^ -o $@ $(INC)


%.o:%.c
    $(CC) $(FPIC) $(CFLAGS) -c $^ -o $@ $(INC)


clean :
    rm -rf *.o
    rm -rf $(target)

C中調用C++中類成員數據(面向對象的數據)

2.2.2 C調用C++中類函數

其實咱們能夠爲類中每一個函數對象都進行接口重構, 而後在參數中加入相似this指針的參數

下面這個示例中, 咱們爲C++MyClass類實現了C的構造和析構函數, 這樣咱們就至關於完成把一個C++寫成的庫徹底作出了一套C語言的調用接口

該示例參見language/c/cpp/c_link_cpp_class

首先是MyClass類, 類中有一個虛函數func

/////////////////////
//  mfileyclass.h
/////////////////////
#ifndef __MY_CLASS_H_INCLUDE__
#define __MY_CLASS_H_INCLUDE__


class MyClass
{
public :
    virtual int func(int);
};


#endif  //  #define __MY_CLASS_H_INCLUDE__


/////////////////////
//  myclass.cpp
/////////////////////
#include <iostream>

#include "myclass.h"


using namespace std;


int MyClass::func(int i)
{
    cout <<"virtual function " <<i <<" in class" <<endl;

    return 0;
}

而後是中間層libmyclass.h, 中間層將C++MyClass類對象進行了一個完整的封裝

  • 經過create_myclass( )構造了一個指向MyClass的指針

  • 經過destroy_myclass( void* thisC)釋放類對象的指針

  • 經過call_myclass_func(MyClass *thismc, int i)調用MyClass::func( )函數

/////////////////////
//  libmyclass.h
/////////////////////
#ifndef __LIB_MY_CLASS_H_INCLUDE__
#define __LIB_MY_CLASS_H_INCLUDE__


#ifdef __cplusplus
extern "C"
{
#endif



void* create_myclass( );

void destroy_myclass( void* thisC);

int call_myclass_func(void *thismc, int i);


#ifdef __cplusplus
}
#endif



#endif  //  #define __LIB_MY_CLASS_H_INCLUDE__



/////////////////////
//  libmyclass.cpp
/////////////////////
#include <iostream>

#include "myclass.h"
#include "libmyclass.h"


using namespace std;



#ifdef __cplusplus
extern "C"
{
#endif


/* extern "C" */void* create_myclass( )
{
    return new MyClass( );
}

/* extern "C" */void destroy_myclass( void* thisC)
{
    delete static_cast<MyClass *>(thisC);
}

/*extern "C"*/int call_myclass_func(void *thismc, int i)
{
    return static_cast<MyClass *>(thismc)->func(i);
}


#ifdef __cplusplus
}
#endif

咱們在主函數中調用了中間層的接口, 來實現C語言中調用C++的接口

/////////////////////
//  main.cpp
/////////////////////
#include <stdio.h>
#include <stdlib.h>

#include "libmyclass.h"

int main( )
{
    void *pclass = create_myclass( );
    call_myclass_func(pclass, 10);
    destroy_myclass(pclass);
    pclass = NULL;


    return EXIT_SUCCESS;
}

最後咱們依舊列出Makefile

#####################
#  Makefile
#####################

#  the compile options
CFLAGS = -Wall -std=gnu99 -O2 -pedantic -Wextra -g
CXXFLAGS = -Wall -std=c++11 -O2 -pedantic -Wextra -g

SHAREDLIB_LINK_OPTIONS = -shared


FPIC = -fPIC

#  the include directory
INC = -I./

target=libmyclass.so libmyclass.a main main_sdk

all:$(target)

main : main.o libmyclass.a
    $(CC) $^ -o $@ -ldl -lstdc++


main_sdk : main.o libmyclass.so
    $(CC) $^ -o $@ -ldl -lstdc++ -L./ -lmyclass


libmyclass.a : myclass.o libmyclass.o
    ar crv $@ $^


libmyclass.so : libmyclass.o myclass.o
    $(CXX) $(SHAREDLIB_LINK_OPTIONS) $(FPIC) $(LDFLAGS) $^ -o $@


%.o:%.cpp
    $(CXX) $(FPIC) $(CXXFLAGS) -c $^ -o $@ $(INC)


%.o:%.c
    $(CC) $(FPIC) $(CFLAGS) -c $^ -o $@ $(INC)


clean :
    rm -rf *.o
    rm -rf $(target)

c_link_cpp_class

2.3 C調用C++的接口總結(wrapper方法和handle方法)

本文給出了一種方法. 基本思想是, 寫一個wrapper文件. 把C++類封裝起來, 對外只提供C語言的接口, 和C++相關的都在wrapper的實現文件裏實現

這裏咱們針對一個完整的類對象, 實現一套接口, 代碼參見language/c/cpp/apple

2.3.1 C++的接口

/////////////////////
//  apple.h
/////////////////////
#ifndef __APPLE_H_INCLUDE__
#define __APPLE_H_INCLUDE__

class Apple
{
public :

    enum
    {
        APPLE_COLOR_RED,
        APPLE_COLOR_BLUE,
        APPLE_COLOR_GREEN,
    };


    Apple();

    int GetColor(void);

    void SetColor(int color);


private:
    int m_nColor;
};


#endif  //  #define __APPLE_H_INCLUDE__
/////////////////////
//  apple.cpp
/////////////////////
#include <iostream>
using namespace std;


#include "apple.h"

Apple::Apple()
: m_nColor(APPLE_COLOR_RED)
{

}

void Apple::SetColor(int color)
{
    this->m_nColor = color;
}

int Apple::GetColor(void)
{
    return this->m_nColor;
}

2.3.2 wrapper接口

/////////////////////
//  applewrapper.h
/////////////////////
#ifndef __APPLE_WRAPPER_H_INCLUDE__
#define __APPLE_WRAPPER_H_INCLUDE__


struct tagApple;


#ifdef __cplusplus
extern "C"
{
#endif

struct tagApple *GetInstance(void);
void ReleaseInstance(struct tagApple **ppInstance);
extern void SetColor(struct tagApple *pApple, int color);
extern int GetColor(struct tagApple *pApple);

#ifdef __cplusplus
}
#endif

#endif  //  #define __APPLE_WRAPPER_H_INCLUDE__
/////////////////////
//  applewrapper.cpp
/////////////////////
#include "applewrapper.h"
#include "apple.h"

#ifdef __cplusplus
extern "C"
{
#endif

struct tagApple
{
    Apple apple;
};

struct tagApple *GetInstance(void)
{
    return new struct tagApple;
}

void ReleaseInstance(struct tagApple **ppInstance)
{
    delete *ppInstance;
    *ppInstance = 0;

}

void SetColor(struct tagApple *pApple, int color)
{
    pApple->apple.SetColor(color);
}

int GetColor(struct tagApple *pApple)
{
    return pApple->apple.GetColor();
}


#ifdef __cplusplus
}
#endif
/////////////////////
//  main.c
/////////////////////
#include "applewrapper.h"
#include <assert.h>

int main(void)
{

    struct tagApple * pApple;

    pApple = GetInstance();

    SetColor(pApple, 1);

    int color = GetColor(pApple);

    printf("color = %d\n", color);
    ReleaseInstance(&pApple);
    assert(pApple == 0);
    return 0;
}

wrapper方法接口

2.3.3 handle接口

其實, wrapper裏的struct徹底能夠不要, 定義一個 handle更好

/////////////////////
//  applehandle.h
/////////////////////
#ifndef __APPLE_HANDLE_H_INCLUDE__
#define __APPLE_HANDLE_H_INCLUDE__

#ifdef __cplusplus
extern "C" {
#endif
int  GetInstance(int *handle);
void ReleaseInstance(int *handle);
extern void SetColor(int handle, int color);
extern int GetColor(int handle);
#ifdef __cplusplus
};
#endif
#endif  //  #define __APPLE_HANDLE_H_INCLUDE__

 

/////////////////////
//  applehandle.cpp
/////////////////////
#ifdef __cplusplus
extern "C" {
#endif

static std::vector<Apple *> g_appleVector;

int GetInstance(int * handle)
{
    g_appleVector.push_back(new Apple( ));
    *handle = 0;
    return 1;
}
void ReleaseInstance(int *handle)
{
    Apple * papple = g_appleVector[*handle];
    g_appleVector.erase(g_appleVector.begin( ) + *handle);
    delete papple;
    *handle = -1;

}
void SetColor(int handle, int color)
{
    g_appleVector[handle]->SetColor(color);
}

int GetColor(int handle)
{
    return g_appleVector[handle]->GetColor();
}


#ifdef __cplusplus
}
#endif

 

/////////////////////
//  mainhandle.cpp
/////////////////////
#include "applehandle.h"
#include <assert.h>

int main(void)
{
    int handle;

    GetInstance(&handle);

    SetColor(handle, 1);

    int color = GetColor(handle);

    printf("color = %d\n", color);
    ReleaseInstance(&handle);
    return 0;
}

 

2.3.4 Makefile

#####################
#  Makefile
#####################

#  the compile options
CFLAGS = -Wall -std=gnu99 -O2 -pedantic -Wextra -g
CXXFLAGS = -Wall -std=c++11 -O2 -pedantic -Wextra -g

SHAREDLIB_LINK_OPTIONS = -shared


FPIC = -fPIC

#  the include directory
INC = -I./


target=main_wrapper


all:$(target)

main_wrapper : main.o libapplewrapper.a
    $(CC) $^ -o $@ -lstdc++

main_handle : main.o libapplehandle.a 
    $(CC) $^ -o $@ -lstdc++

main_wrapper_sdk : main.o libapplewrapper.so
    $(CC) $^ -o $@ -lstdc++ -L./ -lapplewrapper

main_handle_sdk : main.o libapplehandle.so
    $(CC) $^ -o $@ -lstdc++ -L./ -lapplehandle

libapplewrapper.a:apple.o applewrapper.o
    ar crv $@ $^

libapplehandle.a:apple.o applehandle.o
    ar crv $@ $^

libapplewrapper.ao : apple.o applewrapper.o
    $(CXX) $(SHAREDLIB_LINK_OPTIONS) $(FPIC) $(LDFLAGS) $^ -o $@

libapplehandle.ao : apple.o applehandle.o
    $(CXX) $(SHAREDLIB_LINK_OPTIONS) $(FPIC) $(LDFLAGS) $^ -o $@


%.o:%.cpp
    $(CXX) $(FPIC) $(CXXFLAGS) -c $^ -o $@ $(INC)

%.o:%.c
    $(CC) $(FPIC) $(CFLAGS) -c $^ -o $@ $(INC)

clean :
    rm -rf *.o
    rm -rf libapple.so libapple.a
    rm -rf $(target)

3 參照

代碼 描述
1.2 extern 「C」 C++中引入C的庫代碼 language/c/cpp/cpp_link_c
2.1.1 C調用C++的基本成員函數 language/c/cpp/c_link_cpp_func
2.1.2 C語言調用C++重載函數的處理 language/c/cpp/c_link_cpp_overload_func
2.2.1 C調用C++中成員函數 language/c/cpp/c_link_cpp_mem_func
2.2.2 C調用C++中類函數 language/c/cpp/c_link_cpp_class
2.3 C調用C的接口總結 language/c/cpp/apple
相關文章
相關標籤/搜索