好的,这会很长,我提前为此道歉。=)
我需要指出的是,这里使用的代码很遗憾与实际生产代码不完全匹配,出于保密原因,而是为了说明问题、一些经过测试的解决方案以及方便讨论而构建的。在概念层面上,它已经足够相似了,尽管一切都被剥离和简化了。数据保护虽然在现实中是必要的,但在这里却被忽略了。
问题
我们有一个共享对象“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 记录中的相同数据,最好不进行复制。我怎么做?