所以我们在CAPI.h中(没有实现)
struct Message;
typedef struct Message Message;
我们有一个CPP_API.h
namespace Bla {
struct Message {};
}
如何将Bla :: Message与C API中定义的Message相关联?换句话说,使Bla :: Message成为C头中定义的Message的实现?
解决方法:
可以使用继承,从:: Message继承到Bla :: Message.
但是,只有在C API处理:: Message *指针而不是:: Message对象本身时,这才有效.这可能不是问题,因为大多数C库处理不透明指针.
首先,向库用户公开公共C结构(为简单起见,示例不会使用名称空间):
typedef struct CMessage
{
// Public data fields for the C API users.
// Having such is **not** recommended when using this inheritance approach,
// a completly opaque structure is recommended instead.
} CMessage;
然后内部功能应该作为继承自CMessage的Message类的方法实现:
struct Message : CMessage
{
// Fields can be safely added here, assuming one do not ever remove fields off
// CMessage causing the reduction of it's size.
// All the fields defined here should be private to the implementation.
int stuff;
// Construct the Message and perhaps initialize CMessage fields.
Message(int param)
{
this->stuff = param;
}
void DoStuff(int i)
{
// Perform heavy work on i
this->stuff = i * 10;
}
};
然后,应该使用处理base,CMessage,object作为指针的普通C函数将方法导出到外部世界.
CMessage* msg_new(int k)
{
return new(std::nothrow) Message(k);
}
void msg_do_stuff(CMessage* message, int i)
{
return (static_cast<Message*>(message))->DoStuff(i);
}
void msg_free(CMessage* message)
{
delete (static_cast<Message*>(message));
}
注意使用< new>的std::nothrow重载.这是使用失败的分配返回null而不是抛出异常.这是因为C不知道异常,就像C符号一样,异常不能保证是二进制级标准化的,因此将它们传播到外部代码是不安全的.
另一个有趣的方法,不是真正的问题,但仍然有趣的是COM类似的接口.
这是C特定的(因为它使用类),但不像通常的方式导出C类,打破可能对方法名称使用不同符号表示的不同编译器供应商之间的兼容性,导出的虚拟方法表可能具有相同的布局有问题的平台中的所有编译器.这是因为vtable非常简单,因此平台ABI可以定义如何在内存中分层虚拟方法表,或者对如何这样做有共识.
COM方法在Windows世界中很常见,例如Direct3D使用这样的东西与外部世界进行通信.
首先,应使用抽象方法向API用户公开类/结构的布局:
struct IMessage
{
virtual ~IMessage() {}
virtual void Release() = 0;
virtual void DoStuff(int i) = 0;
};
如果不打算破坏二进制兼容性,则不应更改顺序或删除任何方法.同样,如果添加新方法,那些方法应该在界面的最后.
然后应该实现IMessage接口的派生对象:
struct Message : IMessage
{
int stuff;
Message(int param)
{
this->stuff = param;
}
~Message()
{
// Perform cleanup
}
void Release()
{
// Release this Message object
delete this;
}
void DoStuff(int i)
{
// Perform heavy work on i
this->stuff = i * 10;
}
};
为了使API用户能够创建对象,必须导出一个返回接口指针的C函数.
// The user is responsible for releasing the returned object pointer by calling obj->Release();
IMessage* MessageCreate(int param)
{
return new(std::nothrow) Message(param);
}
通常这种方法使得从IUnknown
继承的接口(IMessage)遵循COM模式,但如果意图纯粹类似于COM接口则没有必要.