3

好的,这会很长,我提前为此道歉。=)

我需要指出的是,这里使用的代码很遗憾与实际生产代码不完全匹配,出于保密原因,而是为了说明问题、一些经过测试的解决方案以及方便讨论而构建的。在概念层面上,它已经足够相似了,尽管一切都被剥离和简化了。数据保护虽然在现实中是必要的,但在这里却被忽略了。

问题

我们有一个共享对象“data_provider”,它是从 Ada 代码生成的。Data_provider 有一个内部数据记录,我们需要从许多共享对象“data_user”1 到 n 访问它,这些共享对象也是从(不同的)Ada 代码生成的。这些共享包含类型定义的 Ada 规范,但本质上,出于(有效;估计和基准)性能原因,数据需要跨共享对象边界共享,最好没有不必要的复制。

这些共享对象在编译时或运行时通过 libdl 链接到 c++ 主程序(此处称为“包装器”)(这还不是一成不变的),因此解决方案需要以任何一种方式工作。我应该补充一点,如果数据也可以从 c++ 端检查,这将是有益的,即使我们在那里没有完整的类型 def 可用。

代码可能需要通过 Ada95 编译,尽管 -05可能会在紧要关头工作。-12 不在讨论范围内。平台是 RHEL5 上的 GNAT。

我试过的东西

目前,一个“可行”的解决方案是简单地获取数据记录的地址,将其传递给包装器,将其传递给 data_user 对象,在那里转换地址->访问,并将指针数据复制到内部对象中。这是下面示例代码中实现的方法。不过,额外的副本可能是性能方面的问题。

另一种“有效”的测试方法是简单地让 data_provider 导出变量,而 data_users 导入相同的变量,但这要求它们都在编译时链接,并且它还全局公开数据,这让我感觉比较脏,更别提脆了。

我相信for data'address use addr条款需要在制定时知道地址,因此不起作用..?

其他一些事情已经尝试并被丢弃,但我现在将它们从桌面上搁置一旁。

我希望,结合下面的代码足以得到一些建议;我全是耳朵。如果有什么需要澄清的,请询问。=)

实际上,我宁愿希望我只是愚蠢并在这里遗漏了一些明显的东西。而且我确实意识到这整个混乱并不完全符合良好的编码实践,在 Ada 或其他方面,但我仍然有点坚持它。

包装器.cpp

extern "C"  {
    void update_data( int fa, int fb );
    int get_address( void );
    void set_address( int addr );
    void handle_new_data( void );
}

int main( int argc, char** argv ) {
    int addr;
    addr = get_address();
    set_address( addr );
    for (int i = 0; i < 42; i++) {
        update_data( i, -i );
        handle_new_data();
    }
}

data_types.ads

package data_types is

    -- dummy data type
    -- SIMPLIFIED from actual use case
    type data_t is
        record
            field_a : integer := 16#c0ffee#;
            field_b : integer := 16#c0ffee#;
        end record;

    for data_t use
        record
            field_a at 0 range 0..31;
            field_b at 4 range 0..31;
        end record;

    type data_t_ptr is access data_t;

end data_types; 

data_provider.ads

with    system,
        data_types;
use     system,
        data_types;

package data_provider is
    -- update internal data structure
    -- SIMPLIFIED from actual use case
    procedure update_data
        (   fa : in integer;
            fb : in integer );
    pragma export_procedure
    (   internal => update_data,
        external => "update_data" );


    -- return address to record containing data
    function get_address return system.address;
    pragma export_function
    (   internal => get_address,
        external => "get_address" );

    -- 'dummy' data; this needs to be passed to data_user
    data : data_t;

end data_provider;    

data_provider.adb

with    system;
use     system;

package body data_provider
is
    procedure update_data
        (   fa : in integer;
            fb : in integer )
    is
    begin
        data.field_a := fa;
        data.field_b := fb;
    end ;


    function get_address return system.address
    is
    begin
        return data'address;
    end;

end data_provider; 

data_user.ads

with    system,
        data_types;
use     system,
        data_types;

package data_user
is
    -- set address for the data record
    procedure set_address
        (   addr : system.address );
    pragma export_procedure
        (   internal => set_address,
            external => "set_address" );

    -- use the new data in internal data structure
    -- SIMPLIFIED from actual use case
    procedure handle_new_data;
    pragma export_procedure
        (   internal => handle_new_data,
            external => "handle_new_data" );

    -- 'dummy' data; this needs to be passed from data_provider
    data : data_t;

end data_user;

数据用户.adb

with    system,
        unchecked_conversion,
        data_types;
use     system,
        data_types;

package body data_user
is

    function to_ptr is new unchecked_conversion
        (   source => system.address,
            target => data_t_ptr );

    -- set address for the data record
    procedure set_address
        (   addr : system.address )
    is
        ptr : data_t_ptr;
    begin
        ptr := to_ptr( addr );
        data := ptr.all;
    end;

    -- use the new data in internal data structure
    -- SIMPLIFIED from actual use case
    procedure handle_new_data
    is
    begin
        null;
    end;

end data_user;

TLDR

用 Ada 编写并从 C++ 外部调用的多个共享库需要访问存储在 Ada 记录中的相同数据,最好不进行复制。我怎么做?

4

1 回答 1

4

数据被复制给用户的唯一地方是Data_User.Set_Address. 调用Data_Provider.Update_Data更改副本Data_Provider但对任何用户都没有影响。

为什么不Set_Address存储指针然后Handle_New_Data读取它?

package data_user
is

   --  as before

   data_ptr : data_t_ptr;

end data_user;

.......
package body data_user
is

   .....

   -- set address for the data record
   procedure set_address
     (   addr : system.address )
   is
   begin
      data_ptr := to_ptr( addr );
   end;

   procedure handle_new_data
   is
   begin
      -- work with data_ptr.all
   end;

end data_user;

顺便说一句,您应该使用System.Address_To_Access_Conversions而不是Unchecked_Conversion用于这项工作。

于 2012-09-07T22:03:29.300 回答