将 C 识别的指针类型定义为内部 C++ class

typedefing a pointer recognized by C to an inner C++ class

我有一个 class 想在 C 和 C++ 之间共享,其中 C 只能将其作为指针获取。然而,因为它是一个内部 class ,所以它不能被前向声明。相反,这是我们当前的代码在通用头文件中所做的:

#ifdef __cplusplus
class Container {
public:
    class Object {
        public:
        int x;
    };
};

typedef Container::Object * PObject;
#else

typedef void* PObject;

#endif

这看起来违反了单一定义规则 (ODR),因为 C 和 C++ 使用 #ifdef 看到了不同的定义。但是因为这是一个指针,我不确定这是否会造成真正的问题。 C 仅使用指针将其传递给 C++ 函数,它不会直接对其执行任何操作。例如公共头文件中的这段代码:

#ifdef __cplusplus
extern "C" {
#endif
int object_GetX(PObject pObject);

#ifdef __cplusplus
}
#endif /* __cplusplus */

这是我们在 C++ 文件中实现它的方式:

int object_GetX(PObject pObject) {
    return pObject->x;
}

我的问题是:

  1. 像这样违反 ODR 会导致任何实际问题吗?
  2. 如果是这样,是否有另一种方便的方式将 class 共享到 C?

首先,type-aliasing指针通常是一个麻烦的秘诀。
别这样。

其次,“内”class是一个被过度使用的概念,所以我的第一反应是考虑它是否真的有必要。

如果有必要,您可以定义一个不透明的空类型并从中派生以确保类型安全:

在共享中 header:

struct OpaqueObject;

#ifdef __cplusplus
extern "C" {
#endif

int object_GetX(OpaqueObject* pObject);

#ifdef __cplusplus
}
#endif /* __cplusplus */

在 C++ 中 header:


struct OpaqueObject {};

class Container {
public:
    class Object : public OpaqueObject {
        public:
        int x;
    };
};


实施:

int object_GetX(OpaqueObject* pObject) {
    return static_cast<Container::Object*>(pObject)->x;
}

Would violating the ODR like this cause any real problems?

“真正的问题”?可能是。也许不是。

没有“混合 C/C++ ODR 规则”。规则“包含”在一种编程语言中,规范是为一种编程语言编写的。 C++ 有自己的规则,C++ 有 ODR 规则。 C有自己的规则,C对ODR一无所知。其他编程语言有自己的规则。

您没有违反 C++ 中的 ODR - C++ 在任何地方都看到 Container 的单一定义。

extern "C"中的内容使用C调用约定,我可以假设它也遵循C语言。在这种情况下,您只是违反了规则 https://port70.net/~nsz/c/c11/n1570.html#6.5.2.2p9 。假设这就像 C 中函数的 ODR。

如果不是,则说明您违反了语言之间的某些内容,那就是 ABI。您从 C 端传递 void * 指针的值,您的函数从 C++ 端读取 Container::Object * 指针的值。您的编译器正在使用的特定 ABI 可能允许这样做,但可能未指定。安全的方法是假设它是不允许的。

is there another convenient way to share the class to C?

C 语言也像 C++ 一样提供静态类型检查,但是通过使用 void * 你基本上将其关闭。不要将 C++ 与 C 混用。只需为 C 端起一个不同的唯一名称,您就不会那么困惑了。

此外,pObject vs PObject?你的转变一定要快!

// object.hpp
#ifdef __cplusplus
class Container {
public:
    class Object {
        public:
        int x;
    };
};

typedef Container::Object PObject;

extern "C" {
#endif  

struct C_PObject {
   void *pnt;
};

int object_GetX(struct C_PObject obj);
// ...

// object_c.cpp
PObject *object_from_c(struct C_PObject obj) {
    return reinterpret_cast<PObject *>(obj.pnt);
}

extern "C"
int object_GetX(struct C_PObject obj) {
    return object_from_c(obj)->x;
}