C 语言与 C++ 语言混合编程的方法

在实际的开发工作中,有时会受到现有代码库的约束,必须对 C 和 C++语言进行混合编程。

而要实现混合编程的主要办法就是声明:extern "C"。被它修饰的变量和函数,将会按照 C 语言方式进行编译和连接。

C++调用 C

从 C++调用 C 语言的函数会比较简单,只需要对函数进行如下的修改:

// C function to be called by C and C++
#ifdef __cplusplus
extern "C"{
#endif

void foo_with_C();

#ifdef __cplusplus
}
#endif

通过 #ifdef __cplusplus 这个宏定义,使得 extern "C"声明在 C++时启用、C 时不启用。这样此函数既可用于 C 语言,也可用于 C++语言。

我们在调用标准库的函数时,其实标准库已经把这一步做好了,所以感觉 C++可以透明的调用 C 的函数。

C 调用 C++

而从 C 语言调用 C++会比较麻烦一些,需要针对 C 语言声明各种 API 函数,然后在 API 函数里再用 C++的语法去调用原来的 C++代码。基础的原理也是依赖于 extern "C"这个声明。

以下用一个简单的例子来讲解这个过程。

假设现有的代码库里面,已经有了一个 Employee 的 C++类。以下分别为该类的头文件和 cpp 文件。

// Employee.h
// Pure C++ code
// Only for C++

#ifndef EMPLOYEE_H
#define EMPLOYEE_H

#include <string>

class Employee
{
public:
    Employee(int id, const std::string& first_name, const std::string& last_name, float salary);
    ~Employee();

    int GetID() const;
    void SetID(int val);

    std::string GetFirstName() const;
    void SetFirstName(const std::string& val);

    std::string GetLastName() const;
    void SetLastName(const std::string& val);

    float GetSalary() const;
    void SetSalary(float val);

    void AdjustSalary(float delta);

    std::string Summary() const;

private:
    int id;
    std::string firstName;
    std::string lastName;
    float salary;
};

#endif
// Employee.cpp
// Pure C++ code
// Only for C++

#include <iostream>
#include <sstream>
#include "Employee.h"

using namespace std;

Employee::Employee(int id, const string& first_name, const string& last_name, float salary)
{
    this->id = id;
    this->firstName = first_name;
    this->lastName = last_name;
    this->salary = salary;
}

Employee::~Employee()
{
}

int Employee::GetID() const
{
    return id;
}

void Employee::SetID(int val)
{
    id = val;
}

string Employee::GetFirstName() const
{
    return firstName;
}

void Employee::SetFirstName(const string& val)
{
    firstName = val;
}

string Employee::GetLastName() const
{
    return lastName;
}

void Employee::SetLastName(const string& val)
{
    lastName = val;
}

float Employee::GetSalary() const
{
    return salary;
}

void Employee::SetSalary(float val)
{
    salary = val;
}

void Employee::AdjustSalary(float delta)
{
    salary += delta;
}

string Employee::Summary() const
{
    stringstream ss;
    ss << "ID: " << id << ", Name: " << firstName << " " << lastName << ", Salary: " << salary;
    return ss.str();
}

现在,我们有个 C 语言的项目,需要复用这个类的功能。

为了在 C 语言复用 Employee 类,我们需要创建一个 Wrapper,包括头文件和 cpp 文件。

其中,头文件里面,根据需要声明各个 API 函数。该文件会被 C 语言使用,需要用 extern "C"来声明 API 函数,而且不能使用 C++独有的语法与功能(比如 class)。

本文福利, 免费领取Linux C/C++全栈开发学习资料包、技术视频/代码,1000道大厂面试题,内容包括(C++基础,网络编程,数据库,中间件,后端开发,音视频开发,Qt开发,游戏开发,Linux内核等进阶学习资料和最佳学习路线图)↓↓↓↓有需要的可以进企鹅裙927239107领取哦~↓↓

cpp 文件,包括了各个 API 函数的实现。针对 C++编译器,可以使用 C++的语法与功能。

参考代码如下所示:

// Employee_API.h
// Need to support both C and C++
// Wrap C++ classes into C interfaces
// C calls C++ interfaces by including this file

#ifndef EMPLOYEE_API_H
#define EMPLOYEE_API_H

#ifdef __cplusplus
// Link with C way
extern "C" {
#endif

void* Employee_Create(int id, const char* first_name, const char* last_name, float salary);
void Employee_Adjust_Salary(void* employee, float delta);
void Employee_Summary(void* employee, char* ret);
void Employee_Destroy(void* employee);

#ifdef __cplusplus
}
#endif

#endif
// Employee_API.cpp
// Only for C++

#include "Employee_API.h"
#include "Employee.h"

#ifdef __cplusplus
extern "C" {
#endif

void* Employee_Create(int id, const char* first_name, const char* last_name, float salary)
{
    return new Employee(id, first_name, last_name, salary);
}

void Employee_Adjust_Salary(void* employee, float delta)
{
    Employee *emp = (Employee *)employee;
    if (!emp)
    {
        return;
    }

    emp->AdjustSalary(delta);
}

void Employee_Summary(void* employee, char* ret)
{
    Employee *emp = (Employee *)employee;
    if (!emp)
    {
        return;
    }

    std::string info = emp->Summary();
    info.copy(ret, info.length());
}

void Employee_Destroy(void* employee)
{
    Employee *emp = (Employee *)employee;
    if (emp)
    {
        delete emp;
    }
}

#ifdef __cplusplus
}
#endif

相当于我们把类的成员函数拆出来,变成一个个普通的函数。原来的类实例,则变成一个函数的指针参数。

接下来,我们就可以在 C 语言里面通过这个 Wrapper 来使用 C++的功能了。

下面是个简单的 C 语言例子:

#include <stdio.h>
#include <string.h>
#include "Employee_API.h"

int main(int argc, char const *argv[])
{
    printf("Demo to show how to call C++ class and methods from C.\n");

    void* tom = Employee_Create(100, "Tom", "Jerry", 8888.88);
    if (!tom)
    {
        return 1;
    }

    const int max_len = 256;
    char info[max_len];
    memset(info, '\0', sizeof(info)); 
    Employee_Summary(tom, info);
    printf("%s\r\n", info);

    Employee_Adjust_Salary(tom, 1111.11);

    memset(info, '\0', sizeof(info)); 
    Employee_Summary(tom, info);
    printf("%s\r\n", info);

    Employee_Destroy(tom);

    return 0;
}

至此,代码方面的工作已经完成。接下来有两种方法完成混编的工作。

1. 通过动态链接库.so 文件

第一种做法,是把原来的 C++代码,还有新加的 Wrapper 代码,编译成一个动态链接库;然后 C 语言的代码 link 这个库,得到可执行文件。

具体做法如下:

$ g++ -Wall -fpic -shared Employee.cpp Employee_API.cpp -o libemployee_api.so
$ gcc -Wall -c main.c -o main.o
$ gcc -Wall main.o -o mixed -L . -lemployee_api

这样,得到一个.so 动态链接库,和一个 mixed 程序。

通过指定动态链接库搜索目录,可以启动 mixed 程序:

$ LD_LIBRARY_PATH="$(pwd)"
$ export LD_LIBRARY_PATH
$ ./mixed
Demo to show how to call C++ class and methods from C.
ID: 100, Name: Tom Jerry, Salary: 8888.88
ID: 100, Name: Tom Jerry, Salary: 9999.99

可以看到,程序正常运行、输出期望的结果了。

2. 通过连接 stdc++和.o 文件

第二种做法,则是把各个 cpp 文件都变成.o 文件,最后与 C 语言的.o 文件 link 到一起,得到可执行文件。具体做法如下:

$ g++ -Wall -c Employee.cpp -o Employee.o
$ g++ -Wall -c Employee_API.cpp -o Employee_API.o
$ gcc -Wall -c main.c -o main.o
$ gcc -Wall Employee.o Employee_API.o main.o -o mixed -lstdc++

这样,得到一个 mixed 程序。

直接运行该程序:

$ ./mix
Demo to show how to call C++ class and methods from C.
ID: 100, Name: Tom Jerry, Salary: 8888.88
ID: 100, Name: Tom Jerry, Salary: 9999.99

可以看到,这种方法也是可以正常运行、输出期望结果的。

实际项目中,具体选用哪种方法,可以根据项目的情况决定。但不管用哪种方法,繁琐的 Wrapper 封装成 API 的步骤,是少不了的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值