3

在 Ada95 中使用 C++ 类 / 构造函数和受控类型

我希望能够在我的 Ada 代码中使用 C++ 类。我的目标是让我的 Ada 代码可移植到 Ada95 规范。我不想使用任何 GNAT 或 Ada05 特定方法。

我在 C 中使用带有包装函数的 pragma Import (C) 来实现我的接口。但是我无法弄清楚如何让我的 C++ Ctors/Dtors 被自动调用。我的第一个想法是使用 Ada Controlled Types,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;
4

3 回答 3

1

在 Ada 95 中确实没有任何可移植(语言标准)方法来调用 C++ 类方法。这适用于 C、Fortran 和大多数其他主要编译系统的编程语言,您可能偶尔希望与 C++ 交互。

为了解决这个问题,您必须使用与 C 程序接口时通常用来处理它的相同技术。基本上,任何需要从其他语言调用的东西都必须具有与之关联的 C 可链接 C++ 函数。extern "C"在 C++ 中,这通常意味着应用到它的裸函数或静态成员函数。

Gnat,由于它与 GCC 的紧密集成,有一些更好的工具来处理 C++ 代码。然而,这不是标准的。

请注意,可以通过将对象作为其参数传递来将此类非 OO 调用“引导”到 OO 方法调用中,然后让非 OO 调用对该对象进行适当的方法调用。尽管如此,这意味着您需要为每种方法提供一个这样的引导函数。

于 2012-07-12T15:39:09.603 回答
1

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);

也就是说,我真的不认为你覆盖 AdaObj_t和 C++的方案Foo是一个好主意,因为这两种语言都有权使用隐藏字段。我可以想象 C++ 可能会存储一个指向调度表的指针,并且我确定(在 GNAT 中)Ada.Finalization.Controlled包含实现终结链的链接的子项。其他编译器可能会采用不同的方式(并且 GNAT 在 GCC 4.7 中改变了它的策略)。因此,我将Obj_t包含对 C++ 对象的引用(即由 返回的值Foo())。

于 2012-07-13T19:52:16.227 回答
0

我已扩展您的代码示例并将其上传到BitBucket 上的存储库

它包含 ctor 包装器,包括默认构造函数、复制构造函数和自定义构造函数。我添加了一些代码来提供调试输出。从我可以看到一切都按预期工作,但是创建(自定义 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 代替 access,因为访问常量 Argument 是 Ada 2005 的一个功能,而普通访问不适用于只读 This。

于 2012-07-31T16:59:44.470 回答