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
相关文章
相关标签/搜索