[英]Using a C++ Class in Ada95 / Constructors and Controlled Types
在Ada95 /构造函数和受控类型中使用C ++类
我希望能够在我的Ada代码中使用C ++类。 我的目标是让我的Ada代码可以移植到Ada95规范中。 我不想使用任何GNAT或Ada05特定的方法。
我正在使用pragma Import(C)和C中的包装函数来实现我的接口。 但我无法弄清楚如何让我的C ++ Ctors / Dtors自动调用。 我的第一个想法是使用Ada受控类型,而Initialize会调用Ctor,Finalize会调用Dtor。 这一切都很好,直到我有一个需要我传递参数的Ctor。
foo.h中
class Foo
{
public:
Foo();
Foo(long x, long y, long z);
Foo(const Foo& that);
~Foo();
Foo& operator=(const Foo& that);
long getX() const;
long getY() const;
long getZ() const;
void setX(long x);
void setY(long y);
void setZ(long z);
private:
long mX;
long mY;
long mZ;
};
Foo_Exports.cpp
#include "foo.h"
#include <new>
extern "C"
{
void extFoo_New (Foo* foo) { new (foo) Foo(); }
void extFoo_NewXYZ (Foo* foo, long x, long y, long z) { new (foo) Foo(x,y,z); }
void extFoo_Delete (Foo* foo) { foo->~Foo(); }
long extFoo_getX(const Foo& foo) { return foo.getX(); }
long extFoo_getY(const Foo& foo) { return foo.getY(); }
long extFoo_getZ(const Foo& foo) { return foo.getZ(); }
void extFoo_setX(const Foo& foo, long x) { foo.setX(x) };
void extFoo_setY(const Foo& foo, long y) { foo.setY(y) };
void extFoo_setZ(const Foo& foo, long z) { foo.setZ(z) };
}
cpp.foo.ads
with Ada.Finalization;
with Interfaces.C;
use Interfaces.C;
package Cpp.Foo is
type Obj_t is new Ada.Finalization.Controlled_Type with private;
procedure Initialize (This : in out Obj_T);
procedure Adjust (This : in out Obj_T);
procedure Finalize (This : in out Obj_T);
function Get_X (This : access Obj_T) return Long;
function Get_Y (This : access Obj_T) return Long;
function Get_Z (This : access Obj_T) return Long;
procedure Set_X(This : access Obj_T;
X : in Long );
procedure Set_Y(This : access Obj_T;
Y : in Long );
procedure Set_Z(This : access Obj_T;
Z : in Long );
private
type Obj_t is new Ada.Finalization.Controlled_Type with null record;
for Obj_T'Size use 192;
for Obj_T'Alignment use 8;
pragma Import (C, Get_X, "extFoo_getX");
pragma Import (C, Get_Y, "extFoo_getY");
pragma Import (C, Get_Z, "extFoo_getZ");
pragma Import (C, Set_X, "extFoo_setX");
pragma Import (C, Set_Y, "extFoo_setY");
pragma Import (C, Set_Z, "extFoo_setZ");
end Cpp.Foo;
cpp.foo.adb
with System;
package body Cpp.Foo is
procedure Initialize (This : in out Obj_T) is
procedure ExtFoo_New(Addr : in System.Address);
pragma Import (C, ExtFoo_New "extFoo_New");
procedure ExtFoo_NewXYZ(Addr : in System.Address,
X : in Long;
Y : in Long;
Z : in Long);
pragma Import (C, ExtFoo_NewXYZ "extFoo_NewXYZ");
begin
null; -- **WHAT DO I DO HERE?!**
end Initialize;
procedure Adjust (This : in out Obj_T) is
begin
null; -- TBD copy ctor
end Adjust;
procedure Finalize (This : in out Obj_T) is
procedure ExtFoo_Delete(Addr : in System.Address);
pragma Import (C, ExtFoo_Delete, extFoo_Delete);
begin
ExtFoo_Delete(This'address);
end Finalize;
end Cpp.Foo;
实际上没有任何可移植(语言标准)方法可以从Ada 95中调用C ++类方法。这适用于C,Fortran以及大多数其他主要编译系统的编程语言,您可能偶尔需要与C ++进行交互。
要解决这个问题,你必须使用与C程序接口时通常用来处理它的相同技术。 基本上,任何需要从另一种语言调用的东西都必须具有与之关联的C-linkable C ++函数。 在C ++中,通常表示裸函数或静态成员函数,并且应用了extern "C"
。
由于与GCC的紧密集成,Gnat有一些更好的处理C ++代码的工具。 但是,这不是标准的。
注意,通过将对象作为参数传递,可以将这种非OO调用“引导”到OO方法调用中,然后让非OO调用对该对象进行适当的方法调用。 但是,这意味着每种方法都需要一个这样的自举功能。
Initialize
只会呼吁默认初始化:
O : Obj_t;
并且是应该调用ExtFoo_New
的子程序。 为了创建一个带有初始值的Obj_t
,你需要另一个函数来调用ExtFoo_NewXYZ
,也许
function Create (X, Y, Z : Long_Integer) return Obj_T;
接着
O : Obj_T := Create (41, 42, 43);
也就是说,我真的不认为你覆盖Ada Obj_t
和C ++ Foo
是个好主意,因为这两种语言都有权使用隐藏字段。 我可以想象C ++可能存储一个指向调度表的指针,我肯定知道(在GNAT中) Ada.Finalization.Controlled
的子Ada.Finalization.Controlled
包含实现终结链的链接。 其他编译器也可能采用不同的方式(GNAT在GCC 4.7中改变了策略)。 所以我会让Obj_t
包含对C ++对象的引用(即Foo()
返回的值)。
我已经扩展了您的代码示例并将其上传到BitBucket上的repo
它包含ctor包装器,包括默认构造函数,复制构造函数和自定义构造函数。 我添加了一些代码来提供调试输出。 从我所看到的一切都按预期工作,但Create(自定义ctor包装器)导致两个冗余的复制/删除跃点。
Foo()
Foo(long, long, long)
Foo(const Foo&)
~Foo()
Foo(const Foo&)
~Foo()
Foo()
Foo(const Foo&)
Uninitialized A, should be 0 0 0: 0 0 0
Initialized B, should be 4 5 6: 4 5 6
Copied B to C, should be 4 5 6: 4 5 6
Modified C, should be 4 7 6: 4 7 6
~Foo()
Foo(const Foo&)
Copied C to A, should be 4 7 6: 4 7 6
~Foo()
~Foo()
~Foo()
~Foo()
我的包装器依赖于以下假设:从对象复制的对象内容中移动对象内容并调用复制构造函数是安全的,而不是从真实构造函数创建的真实对象进行复制。
当以某种方式跟踪内存中的C ++对象位置时,此方法在极少数情况下不起作用。 Adjust.Temp_Copy将以这种方式取消计算。
Flat_Get_X使用System.Address而不是访问,因为访问常量Argument是Ada 2005的一个功能,并且普通访问不可用于只读。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.