【问题标题】:Is it possible to include C++ libraries in C programs?是否可以在 C 程序中包含 C++ 库?
【发布时间】:2013-12-17 23:00:09
【问题描述】:

我正在与一个用 C++ 为我们开发库的承包商合作。我想知道是否可以在 C 程序中使用该库。 我使用 Gcc 作为我的编译器。

【问题讨论】:

  • 您需要在C++ 中编写一些包装器,使用extern "C" 公开C 接口。所以不,不是直接的,但是是的,有可能,以某种有限的方式。
  • @BoBTFish that + 用 g++ 编译 main
  • @BЈовић 什么,为什么?如果您只是链接到另一个库,该库公开了一个用extern "C" 声明的接口,为什么还要这样做?
  • 或者,升级到 C++ 作为您的主要语言。大多数 C 代码都可以正常工作,世界将变得更美好。
  • @BЈовић 是的,将 C++ 目标文件编译 到 C 程序中显然是有问题的。但是你可以链接到一个(动态的)C++ 库,只要它公开一个 C 接口

标签: c++ c gcc


【解决方案1】:

是的,这是可能的。然而,正如 BoBTFish 在上面的评论中所说,您(或承包商)必须为 C++ 库设计一个 C 接口:

  • 编写一个可以在 C 和 C++ 中编译的头文件,并声明一些 extern "C" 函数。这些函数的接口必须在 C 中有效,这在 C++ 术语中意味着它们仅使用 POD 类型(例如没有引用)并且不抛出异常。您可以将非 POD C++ 类声明为不完整类型并使用指向它们的指针,因此通常每个非静态成员函数都由一个函数包装,该函数将成为 this 的指针作为其第一个参数。
  • 在 C++ 中实现函数以调用 C++ 库
  • 将库和包装器编译为 C++
  • 将您的程序编译为 C(您可以在需要的地方使用 #include 标头)
  • 用 g++ 将它们链接在一起,以便 C++ 组件可以链接到 libstdc++。

我想你可能争辩说,由于该程序与 g++ 链接,根据定义,它是一个使用 C 库(恰好包含 main)的 C++ 程序,而不是一个 C 程序使用 C++ 库。就我个人而言,我不会费心争论,重要的是你现有的 C 代码都没有改变。

例子:

lib.h

#ifdef __cplusplus
extern "C"
#endif
int foo();

lib.cpp

#include "lib.h"
#include <vector>
#include <iostream>

int foo() {
    try {
        std::vector<int> v;
        v.push_back(1);
        v.push_back(1);
        std::cout << "C++ seems to exist\n";
        return v.size();
    } catch (...) {
        return -1;
    }
}

main.c

#include "lib.h"
#include <stdio.h>

int main() {
    printf("%d\n", foo());
}

构建

g++ lib.cpp -c -olib.o
gcc main.c -c -omain.o
g++ main.o lib.o -omain

如果您确实想在使用gcc 链接和使用g++ 之间做出任意区分,以下也可以代替第三行:

gcc main.o lib.o -llibstdc++ -omain

然而,对于lib.cpp 中所有可能的代码,我不确定gcc -libstdc++ 是否能像g++ 一样工作。我只是为了这个例子测试了它,当然还有很多我没有用过的C++。

【讨论】:

  • -1,因为无法使用 gcc 链接 c++ 库,而您说可以这样做。如果你证明它错了,我会改变主意。
  • @BЈовић:不,问题是如何使用 GCC(好吧,提问者特别说“Gcc”,所以我猜这既不是 GCC 也不是 gcc,而是他们自己的一些命名法)链接一个 C++ 库到一个用 C 编写的程序中。你发明了一个人为的限制。 g++ 是 GCC 的一部分。
  • @BЈовић:我现在包含了一个适用于我的示例代码的gcc 命令行。但由于限制是任意的,与问题无关,我不打算研究它是否总是有效,或者是否有任何情况需要的不仅仅是-llibstdc++g++ 的手册页与 gcc 的手册页相同,它说 gcc 是 GNU C 和 C++ 编译器。我不知道谁告诉过你,或者你想到什么情况下应该避免g++
  • 最后一件事:c和c++是不同的语言。这意味着 gcc 和 g++ 必须遵循不同的规则。我不反对 g++,但有时不可能从 gcc 切换到 g++(大项目,或其他一些奇怪的原因)。
  • @SteveJessop 在这种情况下值得指出的是,C++ 标准中关于混合 C 和 C++ 的语言允许(但不要求)一个实现要求将 main 编译为 C++(如果有)程序中任何位置的任何 C++ 代码。我不记得 GCC 是否在任何当前的操作系统上都需要这个。
【解决方案2】:

这个小的动态库(g++)和程序(gcc)完美的编译和链接:

标题 library.h

#ifdef __cplusplus
extern "C" {
#endif

typedef struct TagPerson {} Person;
extern Person* person_create(const char* name);
extern void person_destroy(Person* p);
extern const char* person_name(Person* p);
extern Person* person_exception(Person* p);

#ifdef __cplusplus
}
#endif

C++ 源代码 library.cc 使用 g++ -shared -fpic library.cc -o libLibrary.so 编译

#include <iostream>
#include <string>
#include <stdexcept>
#include "library.h"

namespace Library {

class Person
{
    public:
    Person(const std::string& name)
    :   m_name(name)
    {
        std::cout << m_name << std::endl;
    }

    const std::string name() const { return m_name; }
    void exception() { throw std::runtime_error(""); }

    private:
    std::string m_name;
};

} // namespace N

extern "C" {

#define PERSON(P) ((Library::Person*)(P))

extern Person* person_create(const char* name) {
    try {
        return (Person*)(new Library::Person(name));
    }
    catch(...) {
        // Error Procession;
    }
    return 0;
}

extern void person_destroy(Person* p) {
    delete PERSON(p);
}

extern const char* person_name(Person* p) {
    return PERSON(p)->name().c_str();
}

extern Person* person_exception(Person* p) {
    try {
        PERSON(p)->exception();
    }
    catch(...) {
        std::cerr << "Exception" << std::endl;
    }
    return 0;
}

} // extern "C"

C 源代码 main.c 使用 gcc main.c -lLibrary -o Test 编译

#include <stdio.h>
#include "library.h"

int main()
{
    Person* p = person_create("Jaco Pastorius");
    printf("%s\n", person_name(p));
    person_exception(p);
    person_destroy(p);
    return 0;
}

【讨论】:

  • 我必须说我不确定它是如何工作的。但是,您有一个 UB。 C 标准规定程序执行从 main 开始,但您的程序在初始化全局对象(包含在 iostream 中)之前开始。
  • @BЈовић 请注意,它是一个隐藏c++部分的动态库。
【解决方案3】:

不,这是不可能的。肯定会错过的一件事是异常处理功能。您必须使用 c++ 编译器编译 main。

如果你真的想用c开发,用c++库,你可以开发一个c库,用g++编译main。


即使编译成功,最后链接也会失败。看 : Why can't I link a mixed C/C++ static library that has a C interface using gcc?
而且它不仅缺少异常功能。还有很多其他的事情,用g++链接一切都可以轻松解决。

正如我上面所说,解决方案是从 main 调用一些函数并将其与 g++ 链接:

#include "my_c_main.h"

int main(int argc, char* argv[])
{
  return run_my_c_main( argc, argv );
}

【讨论】:

  • 好吧,它需要-lstdc++ 之类的东西,但我不会这么快就“不可能”声明(我使用它并且它有效)。虽然实际上不记得了,但我的构建中可能会禁用异常和 RTTI。毕竟,很多库都是用 C++ 编写的,但是通过 C 函数向外部程序公开接口。
  • @keltar 随意添加一个答案,因为我真的很想看看你是如何做到的。据我所知,不使用g++是无法链接c++库的。
  • @SteveJessop 我不是。问题是关于 gcc - 而不是 g++。我说如果他想链接 c++ 库,OP 需要使用 g++。没有办法绕过它。
  • 完全有可能,只是需要努力。
  • @BЈовић 第二个答案已经说明,无需添加更多。但是,是的,我使用 gcc 将我的 C 代码与 ODE 和 squirrel 链接——这两个 C++ 库都带有 extern "C" 接口。添加-lstdc++ 对我来说就足够了——无论是使用 GNU 工具链还是使用 xmingw。
【解决方案4】:

究竟是什么问题?您是否已经交付了“错误”语言的库,或者您的承包商想用 C++ 开发该库但您需要从 C 链接它?

在前一种情况下,假设您正确指定了事情,您应该将其作为承包商的问题,但如果您没有或者如果这不可能,您将需要按照 Steve Jessop 的建议编写一个接口。

在后一种情况下,这真的不是问题:承包商需要确保她以可从 C 调用的形式提供代码,并且可以安全/可靠/稳定地以这种方式调用。完成这项工作的内部代码实际上可以是任何东西,只要她以正确的方式为您包装它。如果您打算自己构建库,则需要它们提供合适的构建脚本,以正确的方式调用编译器。如果您的内部程序员无法处理 C++,我想可能存在维护问题,但这是与招聘/就业有关的问题,而不是其工作原理的技术细节。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-20
    • 2016-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多