【问题标题】:Using a C++ Class in Ada95 / Constructors and Controlled Types在 Ada95 中使用 C++ 类/构造函数和受控类型
【发布时间】:2012-07-12 13:39:45
【问题描述】:

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

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

我在 C 中使用带有包装函数的编译指示导入 (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;

【问题讨论】:

  • Obj_t 需要保留一些 C++ 构造函数的返回值的记录(你为什么要指定'Size 和'Alignment?);我不相信你可以欺骗 Ada 和 C(++) 让两者使用相同的内存区域。 Initialize 仅在默认初始化时调用,并且会调用 ExtFoo_New。你需要另一个 Ada 子程序(函数,我希望)来调用 ExtFoo_NewXYZ。
  • @SimonWright 我正在使用“大小”和“对齐”,以便在声明新的 Obj_T 时可以在堆栈上分配正确数量的数据。您可以通过使用 'Address [for Obj'Address use addr;] 的表示子句来欺骗 Ada 使用 C++ 内存,并且您可以使用 Placement new 语法 [Foo* foo = new (addr) Foo() 在 C++ 中做同样的事情;]。 -- 如果我创建另一个子程序来调用 ExtFoo_NewXYZ,我如何让它被自动调用?我希望我的 Ada 接口看起来像 Ada 代码,而不是 C++ 代码的 Ada 接口。
  • @Jerunh:使用 Swig (swig.org) 怎么样?
  • @wilx 根据 Swig 网页,Ada 没有被列为兼容语言。
  • @Jerunh:啊,对不起。我确信它就在那里。

标签: c++ constructor ada


【解决方案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);

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

【讨论】:

  • 我最终将 Create 方法与 Controlled 类型结合使用,这样我就可以将 Adjust 用于复制构造函数,并将 Finalize 用于析构函数。它不像我希望的那样优雅,但它是可用的最佳解决方案。
【解决方案2】:

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

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

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

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

【讨论】:

  • 我很困惑。这不正是我在 Foo_Exports.cpp 中所做的吗?我将所有 OO 调用封装在裸函数中,并将对象作为参数传入。我的问题实际上是我在 Initialize 函数中做什么来确定我应该进行哪个构造函数调用,以及如何将数据获取到构造函数调用?
【解决方案3】:

我已扩展您的代码示例并将其上传到repo on BitBucket

它包含 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 代替 access,因为访问常量 Argument 是 Ada 2005 的一个功能,而普通访问不适用于只读 This。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-30
    • 2012-06-04
    • 1970-01-01
    • 1970-01-01
    • 2012-05-12
    • 1970-01-01
    相关资源
    最近更新 更多