Обмен данными между общими библиотеками / объектами в Ada95

Хорошо, это будет долго, я прошу прощения за это заранее. знак равно

Я должен отметить, что код, используемый здесь, к сожалению, не совсем соответствует фактическому производственному коду, из соображений конфиденциальности, но создан для иллюстрации проблемы, некоторых проверенных решений и для облегчения обсуждения. На концептуальном уровне это достаточно похоже, хотя все было урезано и упрощено. Защита данных, хотя и необходима в реальности, здесь игнорируется.

Эта проблема

У нас есть общий объект data_provider, который генерируется из кода Ada. Data_provider имеет внутреннюю запись данных, к которой нам нужно получить доступ из нескольких общих объектов, «data_user» с 1 по n, также сгенерированных из (другого) кода Ada. Они разделяют спецификации Ada, содержащие определения типов, но, по сути, данные должны совместно использоваться через границы общих объектов, предпочтительно без ненужного копирования, по причинам (действительным, как оценочным, так и сравнительным) производительности.

Эти общие объекты связаны с основной программой на c ++ (здесь она называется «обертка»), либо во время компиляции, либо во время выполнения, через libdl (это еще не написано в камне), поэтому решение должно работать в любом случае. Я должен добавить, что было бы полезно, если бы данные можно было проверять и с конца c ++, даже если у нас там нет полного определения типа.

Код, вероятно, должен пройти компиляцию Ada95, хотя -05 может быть работать в крайнем случае. -12 со стола. Платформа GNAT на RHEL5.

Материал, который я пробовал

В настоящее время «рабочее» решение состоит в том, чтобы просто захватить адрес записи данных, передать его оболочке, передать его в объекты data_user, преобразовать address-> access там и скопировать данные pointee во внутренние объекты. Этот метод реализован в приведенном ниже примере кода. Хотя дополнительная копия может быть проблемой с точки зрения производительности.

Другой проверенный метод, который «работает», состоит в том, чтобы просто позволить data_provider экспортировать переменную, а data_users импортировать то же самое, но это накладывает требование, чтобы все они были связаны во время компиляции, а также предоставляет данные глобально, что заставляет меня чувствую себя довольно грязно, не говоря уже о том, что оно хрупкое.

я верю for data'address use addr пункты требуют знания адреса во время разработки, и, следовательно, не будут работать ..?

Несколько других вещей были опробованы и отброшены, но я пока оставляю их без рассмотрения.

Я надеюсь, что в сочетании с приведенным ниже кодом будет достаточно, чтобы получить несколько предложений; Я весь во внимании. Если что-то нуждается в разъяснении, пожалуйста, попросите об этом. знак равно

Я на самом деле скорее надеюсь, что я просто глупый и упускаю что-то очевидное здесь. И я действительно понимаю, что весь этот беспорядок не совсем соответствует хорошей практике кодирования, в Аде или где-то еще, но я все еще застрял с этим.

wrapper.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;

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, предпочтительно без копирования. Как я могу это сделать?

3

Решение

Единственное место, где данные копируются пользователю, находится в 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 для этой работы.

4

Другие решения

Других решений пока нет …