C/C++ API难题

6
这是重新编写的一个问题,与先前的版本完全不同;我认为第一个版本省略了重要细节,而这个版本提供了所有上下文。
我有一份C++ API的头文件。该API声明了几个类,如下所示:
class Foo
{
public:
    inline void Bar(void);
    /* more inlines */
private:
    inline Foo();
    /* maybe more inline constructors */
}

即没有成员变量,所有函数都是内联和公共的,除了构造函数。构造函数是私有的,因此,就我所理解的C ++而言,我无法真正调用它们。为了创建这些对象,我应该使用auto_ptr来管理它们:

class FooAutoPtr : public std::auto_ptr<Foo> 
{
public:
    inline FooAutoPtr();
    /* one for each Foo constructors */
}

API还有第二组类似于这样的函数:
void Foo_Bar(void *self);
Foo* Foo_Constructor();

我们称之为“核心函数”,因为它们是从主应用程序实际导出的符号,而不是C++类。核心函数具有C语言链接(即它们被声明为extern "C"),但它们被声明为使用和返回C++类型(例如,它们可以使用引用:Foo &foo)。最后,头文件包含了C++类内联函数的实现。所有这些函数都是相同的:它们调用核心函数。例如,FooAutoPtr构造函数是这样的:
inline FooAutoPtr::FooAutoPtr()
{
   reset(Foo_Constructor());
}

据我所理解,该代码从主机应用程序接收一些对象,这些对象被认为是指向Foo的指针,并将auto_ptr小工具更改为指向此对象。但对于开发人员来说,它看起来像是指向Foo的真正指针。调用Foo::Bar()的方式如下:
inline Foo::Bar()
{
    Foo_Bar(this);
}

这适用于所有的C++类和方法。聪明,对吧?
现在,有人可以解释一下这是什么意思吗? :) 这不是一个真正的C++ API,对吗?对我来说,它更像是一个在C API之上的薄薄的C++包装器。如果是这样,我能重新声明核心函数以去除C++部分吗?我知道如何编写围绕C++的C包装器(实际上,我已经写好了),但是,如果可能的话,我宁愿丢掉包装器直接使用函数。但是如何去掉C++的东西?
例如,可能会有一个带有引用的函数:
Bar& Foo_GetBar(void* self, const Baz& baz, int& i);

现在我从我的C++包装器中这样调用它:

typedef struct bar bar_t; /* and others */
/*...*/
bar_t*
foo_get_bar(foo_t* foo, baz_t* baz, int* i)
{
    return (bar_t*) &Foo_GetBar(foo, *(Baz*)baz, *i);
}

它可以正常运行(我不知道是如何实现的)。但我更希望将其重新声明为:

/* type? */ Foo_GetBar(foo_t*, /* type? /*, /* type? */);

更新: 我发现了一个有趣的事情,证实了尼尔的答案。这是一个使用相同API的Common Lisp代码。(当然,它必须使用C部分。)从源代码中我可以(勉强)读到的是,作者只是在引用处使用指针。以下是将C++声明转换为Lisp的代码片段:

;; use * instead of & - we're not interested in C++ details
line (regex-replace-all "&" line "*")

就是这样 :) 谢谢大家!


8
哇,一个问题被真正地标记为 cc++。这可真是难得的出现…… - ildjarn
2
如果核心函数返回 C++ 类型(引用),那么它就是真正的 C++ API(而不是 C API),只是它不是面向对象的。 - Seth Carnegie
@SethCarnegie,就是这样 :) 你看,标题甚至在核心函数附近有一个注释,说这些是“仅适用于 C 环境”。好吧,又是一个过时的注释 :) - Mikhail Edoshin
但是你的函数 foo_get_bar 仍然是使用 C++ 编译器编译的,对吧? - Krizz
@Krizz 当然可以。但它将其导出为“C”,并且所有类型也都是C,因此我可以从普通的C中调用它。 - Mikhail Edoshin
2个回答

4
在理论上,编译器如何处理C语言链接声明中的引用是一个未指定的实现细节。然而,许多编译器将其视为指针。因此,如果C++库头文件导入Bar& Foo_GetBar(void* self, const Baz& baz, int& i);,那么您可以尝试将其导入到C头文件中作为bar_t* Foo_GetBar(foo_t* self, const baz_t* baz, int* i);,这可能会起作用。

我来试一下。我有点怀疑,但是没有确认就不敢开始 :) 谢谢! - Mikhail Edoshin
顺便提一下,我更新了关于Lisp代码的帖子,我刚刚发现它使用相同的API,而且它确实只是使用指针 :) 所以你是正确的。 - Mikhail Edoshin

1
#define & *    // convert references into pointers.

停。对的。就在那里。那只是一个笑话,而不是试图看看单个答案可以累积多少踩。

但是重写头文件以将引用替换为指针的方法肯定有效。因为这些是C函数,所以不必担心名称混淆,我还没有见过任何一种编译器不将引用实现为指针的情况。(还有其他可能的方式吗?)


#define 只适用于标识符,所以你不能使用那个技巧 :) - Seth Carnegie
@Seth:确实。这其实也很好。 - Roddy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接