具有C连接的不透明结构& C ++实现

时间:2017-12-07 04:16:59

标签: c++ c linker

说我想要一个带有C链接的结构foo。我将不透明地在C风格的头文件(foo.h)中声明它:

struct foo;
typedef struct foo foo;

但我想在foo的实现中使用C ++。假设我希望foo包含std::vector<int>。由于C代码无法访问foo的字段,因此我不明白为什么编译器(或语言标准)应禁止此操作。但是我该怎么做?我可以将extern "C"放在foo.cc中foo的实现中吗?

extern "C" {
    struct foo {
        ....
        std::vector<int> bar;
    }
}

3 个答案:

答案 0 :(得分:0)

只要结构的内容是POD(普通旧数据),结构在C和C ++之间是兼容的。如果您将非POD添加到其中,例如您的向量,它不再与C兼容,并且在与memsetmemcpy等典型C函数一起使用时会导致未定义的行为。非POD成员导致结构的另一个内存布局,它在它的开头获得一个虚拟表以支持继承。

答案 1 :(得分:0)

我最终在C下填充C ++ ......这是它的一般要点。 (另外,让C ++异常解除C堆栈可能会导致C代码出现问题而不知道会发生这样的事情......因此建议在C ++接口函数中执行一些catch(...)块。)< / p>

lib.h :一个头文件,使用C调用约定声明一些函数,无论它是编译为C还是C ++

#pragma once

#if defined(__cplusplus)
extern "C" {
#endif

/* Looks like a typical C library interface */

struct c_class;

struct c_class *do_init();
void do_add(struct c_class *tgt, int a);
int  do_get_size(const struct c_class *tgt);
void do_cleanup(struct c_class *tgt);


#if defined(__cplusplus)
}
#endif

lib.cpp :一个带有几个用C调用约定声明的函数的C ++库

#include "lib.h"

#include <iostream>
#include <vector>
#include <cstdlib>

class Foo
{
  std::vector<int> m_vec;
public:
  Foo() : m_vec() {}
  virtual ~Foo() {}

  void add(int a) {
    m_vec.push_back(a);
  }

  int getSize() {
    return m_vec.size();
  }
};


/* Exposed C interface with C++ insides */
extern "C" {

  struct c_class
  {
    Foo *guts;
  };

  struct c_class *do_init()
  {
    struct c_class *obj = static_cast<c_class*>(malloc(sizeof(struct c_class)));
    obj->guts = new Foo();
    return obj;
  }

  void do_add(struct c_class *tgt, int a) {
    tgt->guts->add(a);
  }

  int do_get_size(const struct c_class *tgt) {
    return tgt->guts->getSize();
  }

  void do_cleanup(struct c_class *tgt) {
    delete tgt->guts;
    free(tgt);
  }
}

main.c :使用从lib导出的C调用约定函数的C程序

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

int main(int argc, char *argv[])
{
  int i;
  struct c_class *obj;

  obj = do_init();

  for(i = 0; i< 100; i++)
  {
    do_add(obj, i);
  }

  printf("Size: %d\n", do_get_size(obj));

  do_cleanup(obj);
}

Makefile :将C构建为C,将C ++构建为C ++的makefile,然后使用C ++编译器进行链接

CXXFLAGS ?= -Wall -Werror -pedantic
CFLAGS ?= -Wall -Werror -pedantic

.PHONY: all
all : test

test: lib.o main.o
    $(CXX) $(CXXFLAGS) -o test lib.o main.o

lib.o: lib.cpp lib.h
    $(CXX) $(CXXFLAGS) -c $< -o $@

main.o: main.c lib.h
    $(CC) $(CFLAGS) -c $< -o $@

clean:
    -rm lib.o main.o test

<强>输出

$ make
g++ -Wall -Werror -pedantic -c lib.cpp -o lib.o
cc -Wall -Werror -pedantic -c main.c -o main.o
g++ -Wall -Werror -pedantic -o test lib.o main.o

$ ./test
Size: 100

答案 2 :(得分:0)

结构本身只是一种类型,换句话说,一种语法糖,除了编译之外不存在作为结构,但只是内存中的几个字节和指向它的指针。因此它没有任何特定于语言的链接。字节的含义由编译器在编译时定义,并且在那里也创建对它们的引用。因此,它的布局取决于您使用的语言。

回答你的问题,std :: vector实现包含某些字段,这些字段成为struct layout的一部分。 c ++理解它们,因为它理解模板和所有其他面向对象的东西,'c'将首先在模板上以及其他c ++ sness上扼制。因此,如果您使用非POD数据,则结构定义不兼容。

如果您使用POD,标准'c'数据类型,没有成员函数和没有位字段,您应该在定义中设置和兼容。这意味着两个编译器都将编译结构,并且布局将类似,以便您可以跨语言保存/恢复它。

如果你所说的只是通过'c'代码将指针传递给c ++结构,那么你应该在任何情况下全部设置。您可以使用强制转换为'void *'以标准方式执行此操作。