代码之家  ›  专栏  ›  技术社区  ›  anatolyg

从C库调用C++函数指针

c++
  •  5
  • anatolyg  · 技术社区  · 15 年前

    我有一个类,它只有静态成员。

    我想注册一个成员函数(下面代码中的verifyclean),在退出时使用“at exit”库函数调用它。

    这个 C++ FQA 表示必须为要以这种方式注册的函数指定extern“c”,如下例所示。

    class Example
    {
    public:
        static void Initialize();
        static void DoDirtyStuff {++dirtLevel;}
        static void CleanUpStuff {--dirtLevel;}
    private:
        static void VerifyClean();
        // DOESN'T COMPILE: extern "C" static void VerifyClean();
        static int dirtLevel;
    }
    
    int Example::dirtLevel;
    
    extern "C" void Example::VerifyClean() // DO I NEED extern "C" HERE?
    {
        assert(dirtLevel == 0);
    }
    
    void Example::Initialize()
    {
        dirtLevel = 0;
        atexit(&VerifyClean);
    }
    

    我真的要用外部的“C”吗?

    如果我用一个非库函数(在普通C语言中实现)替换“atexit”,答案会改变吗?

    如果函数VeFIFY Cube是公开的,我决定直接从C++代码调用它,我会得到链接错误或运行时崩溃吗?我之所以这么做是因为声明根本没有提到外部“C”,所以常规C++代码可能会错误地处理函数调用。这在我的MS Visual Studio 2005系统上工作正常。

    4 回复  |  直到 12 年前
        1
  •  7
  •   John Millikin    15 年前

    它是 可能的 对于编译器来说,对于C和C++代码使用不同的调用约定;然而,在实践中,几乎从不发生这种情况。

    如果您只想让它工作,而不关心支持晦涩的编译器,那么不要费心 extern "C" . 在任何广泛使用的编译器中都没有必要这样做。

    如果您想要完全学究,或者需要支持学究编译器,请编写一个包装器:

    extern "C" static void ExampleVerifyClean()
    {
      Example::VerifyClean();
    }
    
    void Example::Initialize()
    {
        dirtLevel = 0;
        atexit(&ExampleVerifyClean);
    }
    
        2
  •  1
  •   Paul Nathan    15 年前

    链接错误。

    C++执行名为Mungle的操作,它生成具有类型信息的链接时间函数名。

    外C转那个 远离的 更简单的标识符。

    编辑:

    如果所有的东西都是由C++编译器编译的,那就不是问题了。但是如果你有一个由C编译器编译的对象文件和一个由C++编译器编译的对象文件,那么你将有一些问题。

    我似乎还记得需要外部“C”规范的DLL,但此时内存可能已经使用了10年。


    可以。

    我创建了一个带有签名的函数的测试用例

    内部FOO(浮动、浮动)

    并根据3个不同的GCC调用进行编译-

    gcc test_c.c -S
    g++ test.cpp -S
    

    这两个调用产生 程序集中的不同标识符 . C++在其通常的类型修改方法中已经损坏了名称。(当然,编译器可能会做不同的事情)

    然后,我包裹起来 foo 在extern“c”中,再次调用g++…

    g++ test.cpp -S
    

    然后 删除已损坏的C++名称 ,留下一个普通的C未组合的名称。

    虽然这里还涉及到其他一些微妙的方面,例如,推到堆栈上的参数的顺序,但我将基于数据的这一点作为我的论据。

        3
  •  0
  •   arifwn    15 年前

    如果没有extern“c”,您的函数名将被编译器破坏,因此函数名最终可能与您期望的不同。您需要使用其损坏的名称(如在Windows中使用GetProcAddress)调用函数,否则将出现链接器错误。不同的编译器以不同的方式损坏它,所以最好继续使用extern关键字。

        4
  •  0
  •   Yang    12 年前

    您可以使用这个:

    class yourname
    {
        public:
        ...
        static void _cdecl AtExitCall ();
    };
    
    int main()
    {
        ataexit( yourname::AtExitCall );
    }