c++ - 使用模板摆脱动态链接样板​​ : extern "C" and templates don't mix;

标签 c++ templates standards dynamic-linking

我想出了一些 C++ 代码,一开始我并没有考虑太多,直到我注意到 clang 接受它,但 gcc 不接受。我越想越觉得 gcc 的说法可能是正确的。问题是,我真的想不出一种好的、合法的方法来做我想做的事情,而不需要大量的样板文件。

首先,这是我希望在 g++ 中编译的代码:

extern "C" template <typename T>
using factory_pointer_t = T* (*)();

因为我认为这可能是一个 XY 问题,所以有一点背景知识。我正在开发一个具有相当简单界面的小插件系统。基本上它只包含一个返回指向抽象基类的指针的工厂函数。抽象基类需要处理内存管理的方法,类似于 COM。像这样的事情:

class ITest {
public:
    virtual void release() = 0;
    virtual void test_method() = 0;
protected:
    virtual ~ITest(){}
};

这将在动态库中实现,如下所示:

class Test : public ITest {
public:
    virtual void release() override {
        delete this;
    };
    virtual void test_method() override {
        std::cout << "Hello, shared World!\n";
    }
};

extern "C" ITest * testFactory() {
    return new Test{};
}

我对此所做的研究表明,这应该至少在 Windows 和 Linux 上跨工具链可靠地工作。即使事实并非如此,该系统也旨在在一个工具链上进行快速原型(prototype)设计,并有机会放弃插件并将所有内容静态链接到某处。到目前为止没有任何问题。

我的其他研究表明 extern "C"链接说明符对于动态链接在支持相同 C ABI 的所有工具之间正常工作非常重要。现在的问题是这样的:

我想编写一些模板代码来帮助简化这一切。 但没有 extern "C" 的组合与模板语法一起似乎可以工作。我向 Google 寻求帮助的努力因有关已弃用的 extern template 的文章而受挫。 。我想给它一个抽象接口(interface)的名称,并让它加载 dll 并解析工厂函数的符号。但正如您所看到的,回调的类型 ( factory_pointer_t<T> ) 由它们返回的抽象接口(interface)指针类型参数化。所以我的问题是:

  1. 这个模板 using 指令是否应该真正编译? (它适用于 clang,但不适用于 gcc)

  2. 如果我将函数指针类型声明为: extern "C" typedef void* (*factory_pointer)();并使用 reinterpret_cast<> 显式转换返回值,我会调用未定义的行为吗?鉴于在 DLL 中,我希望保持导出的函数签名不变。

  3. 为了实现我既定的目标,我是否还有其他可能没有考虑过但不涉及 void* 的选择? ?

  4. 是一个带有 extern "C" 的 typedef对于函数指针来说是绝对必要的吗?标准似乎还没有下定决心。例如:在 2014 年标准草案的 7.5 中:“两种不同语言的函数类型 链接是不同的类型,即使它们在其他方面相同。”但在 8.3.5 第 8 段中:“返回类型、参数类型列表、引用限定符和 cv-限定符-seq,但不是默认值 参数 (8.3.6) 或异常规范 (15.4) 是函数类型的一部分。”

我真的很想让它在其他编译器中自动工作(即我给它一个类型并告诉它要加载什么库,我就完成了),但我想我没有看到我需要做什么。任何涉及每个类超过一行样板的内容,我都会认为很糟糕。

由于这是我第一次尝试动态链接(不仅仅是让构建系统为我做这件事),我渴望得到一些好的建议。

总结:我有一个基于第一行代码的工作解决方案,该解决方案仅适用于一个编译器。我知道这可能是错误的,或者至少不是普遍支持的语法。我该如何正确地做到这一点?

最佳答案

  1. Should this template using directive actually compile? (It does on clang but not gcc)

我不确定。该标准规定函数类型、函数名称和变量名称具有语言链接,但没有说明是否可以使用别名模板来生成此类函数类型。

EDG 也拒绝了它,但 Clang 允许它可能是正确的。

编辑:我找不到 http://open-std.org/JTC1/SC22/WG21/docs/cwg_closed.html#1463其中指出 [temp]/4 表示

A template, a template explicit specialization (14.7.3), and a class template partial specialization shall not have C linkage.

所以从技术上讲,Clang 接受它是错误的,但进化工作组将考虑是否应该允许它。

  1. If I declare my function pointer type as: extern "C" typedef void* (*factory_pointer)(); and cast the return value explicitly with a reinterpret_cast<>, will I be invoking undefined behavior?

(编辑:我最初在这里说是,因为我读错了问题)

不,但是 static_cast会比 reinterpret_cast 更好.

  1. Do I have any other options I might not have considered for accomplishing my stated goal that don't involve void*?

摆脱所有C语言链接。我认为这对您的情况没有必要或有用。

  1. Is a typedef with extern "C" for the function pointer strictly necessary? The standard can't seem to make up its mind. It says for example: in 7.5 of the 2014 draft standard: "Two function types with different language linkages are distinct types even if they are otherwise identical." but in 8.3.5 paragraph 8: "The return type, the parameter-type-list, the ref-qualifier, and the cv-qualifier-seq, but not the default arguments (8.3.6) or the exception specification (15.4), are part of the function type."

这没有说明任何有关语言链接的内容,因此与 7.5 中非常明确的声明(即它是类型的一部分)并不矛盾。

另请注意:

[expr.call]/1: Calling a function through an expression whose function type has a language linkage that is different from the language linkage of the function type of the called function’s definition is undefined (7.5).

这是未定义的,因为如果例如 C 语言链接意味着与 C++ 语言链接不同的调用约定,它将不起作用。

但是大多数编译器都会做正确的事情,因为它们实际上并没有对 C 和 C++ 函数使用不同的调用约定,而且大多数编译器甚至没有实现 C 语言链接的函数类型不同的规则。参见例如https://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316

关于c++ - 使用模板摆脱动态链接样板​​ : extern "C" and templates don't mix;,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28129777/

相关文章:

c++ - 如何在Mac的CLion中添加开源库?

c++ - 如何修复 "Error LNK2019 : unresolved external symbol ... "

python - 在 Python 中使用可变数量的 .format() 参数进行格式化

c++ - 没有定义的结构模板的目的是什么?

c++ - 为什么标准的 C++ 容器适配器不提供明确的功能?

c++ - 编译器警告使 int8_t 的复合赋值提升为 int 感到困惑

c++ - 从 vector 数组循环中获取数据供以后使用 C++

c++ - 如何仅根据第二个字符串对字符串 vector 的 vector 进行排序

ruby-on-rails - 需要帮助理解此错误 - Pages#show 中的 SyntaxError

c++ - 在C/C++中写一个非打印字符的行为是什么?