C++外部声明隔离

C++ External Declaration Isolation

本文关键字:隔离 声明 外部 C++      更新时间:2023-10-16

请考虑以下事项:

namespace N {
    extern "C" void f();
}
void g() {
    N::f();
}

此代码声明命名空间内具有 C 链接的外部函数。这使得从私有命名空间引用此类函数成为可能,从而避免了由普通全局外部声明引起的命名空间污染。它还允许客户端代码为同一函数发出其他(希望兼容的(声明而不会发生冲突,即使在全局命名空间中也是如此,可能源自供应商提供的标头包含。

我经常依赖 C 和 C++ 中的类似构造将编译与某些库提供的编写不良或冲突的头文件隔离开来。(在 C 语言中,这是通过在函数作用域发出所需的声明来实现的,如果不是在函数作用域不允许extern链接声明,这在C++中也是可能的。这对于正确链接到定义良好的 ABI 特别有用,而不必依赖供应商提供的头文件。

是否可以对具有常规C++链接的函数或方法执行相同的操作?也就是说:在私有命名空间(或任何类型的本地范围内(声明具有C++链接的外部函数,但该函数可能是指在另一个命名空间中实际定义的函数?

预期功能(伪代码(:

namespace N {
    // Actually should link with P::f() (and not N::f()).
    extern "C++" void f();
}
void g() {
    N::f(); // P::f();
}

对于源文件(与头文件相反(,这显然不是问题,因为在这种情况下命名空间污染无关紧要。因此,此问题主要涉及隔离库头文件中的声明(用于模板和内联函数(。

欢迎特定于编译器的解决方案(MSVC和GCC感兴趣(。

示例:假设我的库名为 Lib1,我想声明 Lib1 命名空间中的所有内容。

// Lib1.hpp
namespace Lib1 {
    class Class1;
    void func1();
    // ...
}

现在假设我的库引用了另一个库,Lib2 ,这是其他人提供的 C 库。

/* Lib2.h */
#ifdef __cplusplus
extern "C" {
#endif
struct Struct2;
void func2();
/* ... */
#ifdef __cplusplus
}
#endif

在我的库中,如果出于某种原因需要,我可以引用Lib2中的实体,而不必包含Lib2.h

// Lib1.hpp
namespace Lib1 {
    extern "C" void func2();
    inline void inlineX() {
        func2();
    }
}

同时,客户端代码可以自由地包含Lib1.hppLib2.h(考虑到它是C++友好的(,而不会发生冲突。

现在,假设有第三个库 Lib3 ,它是一个C++库,并在 Lib3 命名空间中声明实体。

// Lib3.hpp
namespace Lib3 {
    class Class3;
    void func3();
    // ...
}

有没有办法以与Lib2相同的方式与Lib3联系起来?也就是说:引用Lib1.hpp内部Lib3实体,不包括Lib3.hpp但仍然允许客户端代码同时包含Lib1.hppLib3.hpp,没有麻烦?

如果在 Lib1 中声明:

// Lib1.hpp
namespace Lib3 {
    void func3();
}
namespace Lib1 {
    inline void inlineY() {
        Lib3::func3();
    }
}

那么如果客户端代码同时包含Lib1.hppLib3.hpp,则可能会发生冲突 - 当然不是在这个声明相同的简单示例中,但实际情况中的细微差异可能会在语法级别触发警告或错误,即使基础 ABI 相同,因为这违反了不声明 Lib1 命名空间之外的任何内容的前提。

希望这有助于理解这个问题。

这不正是 using 指令在C++中的预期用法吗?

// Lib3.hpp
#pragma once
namespace Lib3 {
    void func3();
}
// Lib1.hpp
#include <Lib3.hpp>
namespace Lib1 {
    using Lib3::func3;
}