首页 > 解决方案 > 在混合代码库中将 C 转换为 C++ 时出现许多包含错误

问题描述

我对 C++ 有点陌生,我有一个关于将 C 代码转换为 C++ 代码以及混合 C 和 C++ 代码的问题。

例如,我将以前的 C 文件重构为 C++ 文件,因为我现在需要在头文件的结构中使用 std::string。现在看起来如下(firstClass.hpp):

struct firstClass
{
              .
              .
              .

    std::string test_string;

    firstClass();
    firstClass(const firstClass &c);
    ~firstClass();
};

结果,这里是first-class.cpp

#include "firstClass.hpp"

extern "C"
{
              .
              .
              .

     #include <errno.h>
     #include <assert.h>
     #include <stdlib.h>
     #include <string.h>
}

              .
              .
              .

现在这是我的问题:在我将其转换为 C++ 代码之前,我有其他文件之前包含firstClass.h注意:这是 C 变体)——这些文件是否也需要转换为 C++ 代码?另外,如果附加文件包括上述这些文件,它们是否也需要转换?我想总结一下我的问题:在将这个初始文件转换为 C++ 之后,我还需要转换这些文件在包含链的下游多远?

标签: c++cc++11

解决方案


大多数 C++ 编译器将同时处理 C 源代码文件(扩展名为 .c 的文件)和 C++ 源代码文件(扩展名为 .cpp 的文件)。因此,转换的第一步是更改为 C++ 编译器并按顺序获取项目文件和/或生成文件,以便全部编译。

接下来是通过源代码库确定哪些数据结构将要更改,哪些包含文件(通常是扩展名为 .h 的文件)具有需要更改的这些数据结构。

在这一点上,您将需要开始划分哪些源是 C++ 和哪些源是 C。例如,虽然 Cstruct是 C++ 的子集,但 C struct++struct允许 C 不允许的构造函数和解构函数。这意味着您可以将 Cstruct与 C++ 源代码一起使用,但不能将 C++struct与 C 源文件中的 C++ 功能一起使用。

其他 C++ 语言关键字,例如class只是不适用于 C 源代码文件。

接下来是查看您将使用哪些 C++ 功能,例如模板和 C++ 标准库功能,例如std::string。同样,您需要将 C++ 源代码与 C 分开。

extern "C"功能允许您通过将函数或变量名称声明为 C 而不是 C++ 来将 C 函数与 C++ 源代码一起使用。这是必要的,因为 C++ 编译器会进行名称修改,使用算法为允许函数重载的函数生成修改后的名称。请参阅什么是名称修饰,它是如何工作的?

您将面临的另一件事是 C++ 引入了大多数标准 C 包含文件的新版本,而没有 .h 扩展名。您可以将旧版本与 C++ 一起使用,但新版本是首选,因为它们适用于 C++。但是,C 源文件只能使用这些文件的旧的、扩展名为 .h 的版本。

头文件的条件编译

允许使用带有 C++ 源代码的旧标准 C 包含文件的神奇之处在于,大多数编译器都有一个特殊#define的可用于进行条件编译的特殊功能。使用 Visual Studio 编译器的特殊定义是__cplusplus,它可以使用如下:

#if defined(__cplusplus)
extern "C" {
#endif

// The type CONNENGINEHANDLE is for future expansion to allow multiple
// sockets to be managed by the dll.
typedef unsigned short CONNENGINEHANDLE;

// following functions are used with a server implementation.
// these functions will setup the parameters for the server and
// start the listen needed to accept a connection from a client
CONNENGINE_API int fnConnEngineSetDomainNamePort(CONNENGINEHANDLE hConnEngineSocket, char *aszDomainName, int nPortNo);
CONNENGINE_API int fnConnEngineStartEngine (int nPort, HWND hWinHandle, UINT wReceiveMsgId);
CONNENGINE_API int fnConnEngineStopEngine ();

#if defined(__cplusplus)
};
#endif

作为包含文件一部分的上述预处理器代码的作用是允许 C++ 源代码文件包含与 C 源代码文件相同的头文件,但是当它包含在 C++ 文件中时,它是extern "C" {头文件文本的一部分因为预处理器有__cplusplus定义。因此,C++ 源代码能够使用 C 源代码中定义的函数,并且对于那些声明在大括号内的函数,通常使用 C++ 完成的名称修饰被关闭。

然而,尽管这种特殊#define的检测 C++ 源文件是否正在由预处理器处理,但您仍然不能在 C 源文件中使用 C++ 语言结构。您仍然需要从 C 中分离出 C++。

C 调用 C++ 功能的接口函数

在某些情况下,您可能需要从 C 源代码功能执行 C++ 源代码功能。在这些情况下,您将需要为 C++ 功能创建与 C 兼容的接口。

例如,如果您的 astruct包含std::string与 C 源代码不兼容的 ,您可以创建 C++ 源代码以隐藏实现细节,然后提供与 C 兼容的接口。

例如,从显示上述用法的包含文件示例中__cplusplus,这些函数之一在 C++ 源代码文件中定义如下。这个 C++ 源代码文件还包含与 C 源代码文件和上面的函数声明相同的头文件fnConnEngineStartEngine(),C++ 编译器知道它不应该命名 mangle 函数名fnConnEngineStartEngine{}。此 C++ 源代码提供了使用 C++ 对象的 C 源之间的接口theApp

CONNENGINE_API int fnConnEngineStartEngine (int nPort, HWND hWinHandle, UINT wReceiveMsgId)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    if (hWinHandle == 0)
        hWinHandle = theApp.m_hWinHandle;

    if (wReceiveMsgId == 0)
        wReceiveMsgId = theApp.m_wReceiveMsgId;

    theApp.StartEngineAsServer (nPort, hWinHandle, wReceiveMsgId);
    return 0;
}

推荐阅读