11

只是想确认在 Windows 环境下,VSTS 2008 + C++ 项目,我们只能将 extern C 应用到函数级别,不能应用到类级别(以便类中的所有成员函数都使用 C 语言名称修饰)?我尝试了几种方法,但总是编译错误。

提前谢谢,乔治

4

5 回答 5

13

您可以extern "C"通过一个非常复杂(但完全合法)的hack来申请成员函数:

extern "C" typedef int bar_t(int x);

struct foo {
     bar_t bar; // yes, this declares a nonstatic member function!
};

int foo::bar(int x) { return x; } // definition

根据 ISO C++03 9.3[class.mfct]/9,这是可能的:

可以使用函数类型的 typedef 声明(但未定义)成员函数。结果成员函数的类型与显式提供函数声明符时的类型完全相同,请参见 8.3.5。

但是,由于 ISO C++03 7.5[dcl.link]/4,这并不能真正为您买任何东西:

对于类成员的名称和类成员函数的成员函数类型,AC 语言链接被忽略。

于 2009-12-23T00:58:18.570 回答
5

extern "c" 使用 c 风格的链接;也就是说,原始函数名是从库中公开的。因为它只是一个原始函数名称,所以没有任何 C++ 独有的功能可以使用它,包括名称空间、类、结构或联合中的方法或外部数据成员。

澄清:结构和联合在 C 中,但没有成员函数,因此它们在 C++ 中的成员函数不能以 c 样式导出(结构和联合定义不需要导出,因为它已经在标题中)

于 2009-06-22T03:32:55.187 回答
5

查看您对先前答案的评论(“[M]y 的问题是我们是否可以extern C在类级别应用,以便类中的所有函数自动具有 C 样式名称修饰?”,答案是 'extern "C"不完全那样工作。

从语法上讲,extern "C"可以应用于花括号分隔块的单个语句:

extern "C" int my_foo(int i)
{
    ...
}

extern "C" {
    int my_bar(int i)
    {
        ...
    }

    int my_baz(int i)
    {
        ...
    }
}

在整个 C 头文件上使用extern "C"适当的保护是很常见的。#ifdef __cplusplus

从语义上讲,应用的实际效果extern "C"只适用于“普通”(即非类)函数和函数指针。当然,您不能将其应用于 C++ 模板。您也不能将它应用于类方法(因为类方法需要知道调用了哪个对象,而 C 样式的链接没有任何方法可以将该信息传递给函数)。

可以应用于命名空间extern "C"存在的函数,但命名空间信息在通过 C 使用时会简单地消失。


更新

如果您已经有一个类(为简单起见,我们将使用POD 类),并且您想让它在 C 中可用,您需要应用extern "C"到 C 中可调用的函数。不幸的是,即使在简单的情况下,这也会变得丑陋:

// in the header file
#ifdef __cplusplus
namespace X {
#endif
    struct A
    {
        int x;
#ifdef __cplusplus
        A() : x(5) { }
        int foo()
        {
             return x += 5;
        }
#endif
    };
#ifdef __cplusplus
    extern "C" {
#endif
        int A_foo(struct A a);
        struct A A_create();
#ifdef __cplusplus
    }
}
#endif


// in the .cc file
#include "try.h"

namespace X {
    extern "C" {
        int A_foo(A* a)
        {
            return a.foo();
        }

        A A_create()
        {
            return A();
        }
    }
}

// in the .c file
#include <stdio.h>
#include "try.h"

int main()
{
    struct A a = A_create();
    printf("%d", A_foo(a));
}

使用 gcc 你可以编译如下:

  • C++ 文件:g++ try.cc -c -o try.o
  • C文件:gcc try.c try.o

有几点很重要:

  • 如果您的 C++ 文件在后台调用 STL,或者调用newor delete(or new[]or delete[]),您将需要将最终程序链接到 C++ 运行时库(gcc 中的命令行开关是-lstdc++.
  • 在编译 C 和 C++ 代码时,您可能想要传递相同的优化标志(优化会影响对象的大小,如果大小不匹配,您会遇到很多麻烦)。多线程同上。
  • 您可以在 C++ 代码中使用任何您想要的异常,但是一旦它们跨过 C 代码,所有的赌注都没有了
  • 如果您想要更复杂的东西,您可能需要查看 PIMPL 模式。
  • 当结构超出 C 代码的范围时,不会调用 C++ 析构函数(一些编译器可能会承诺这样做,但这不是标准的)。如果您需要对这些对象进行任何清理,您需要调用调用析构函数的 extern "C" 函数。

显式调用析构函数:

extern "C" void A_destroy(struct A a)
{
    a.~A();
}
于 2009-06-22T04:02:53.367 回答
4

恐怕不是。但是如果你想将 C++ 的对象传递给 C 函数,你可以参考这个链接: http: //www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html#faq-32.8

于 2009-06-22T03:33:31.177 回答
2

嗯...extern "C"强制 C 样式的链接。它不能与 AFAIK 类一起使用。

于 2009-06-22T03:19:02.780 回答