Note that there are some explanatory texts on larger screens.

plurals
  1. POSharing data across shared libraries/objects in Ada95
    primarykey
    data
    text
    <p>Okay, this'll be a long one, I apologise for that in advance. =)</p> <p><strong>I need to point out that the code used here unfortunately does not exactly match the actual production code, for confidentiality reasons, but is constructed to illustrate the problem, some tested solutions, and to facilitate discussion. On a conceptual level, it's similar enough, although everything has been stripped down and simplified. Data protection, although necessary in reality, has been ignored here.</strong></p> <h2>The problem</h2> <p>We have a shared object, 'data_provider', which is generated from Ada code. Data_provider has an internal data record, which we need to access from a number of shared objects, 'data_user' 1 through n, also generated from (different) Ada code. These share Ada specs containing type defs, but essentially the data needs to be shared across shared object boundaries, preferably without unnecessary copying, for (valid; both estimated and benchmarked) performance reasons.</p> <p>These shared objects are linked to a c++ main program (called 'wrapper' here), either compile-time or at run-time, via libdl (this isn't written in stone yet), so the solution needs to work either way. I should add that it is beneficial if the data can be inspected from the c++ end as well, even if we don't have the complete type def available there.</p> <p>The code likely needs to pass Ada95 compilation, although -05 <em>might</em> work in a pinch. -12 is off the table. Platform is GNAT on RHEL5.</p> <h2>Stuff I've tried</h2> <p>Currently, a 'working' solution is to simply grab the address of the data record, pass it out to the wrapper, pass it into the data_user objects, convert address->access there, and copy the pointee data into internal objects. This is the method implemented in the example code below. The extra copy may be a problem performance-wise though. </p> <p>Another tested method that 'works' is to simply let the data_provider export the variable, and the data_users import the same, but this imposes a requirement that they all be linked at compile-time, and it also exposes the data globally, which makes me feel rather dirty, not to mention it's brittle. </p> <p>I believe <code>for data'address use addr</code> clauses would require knowing the address at elaboration time, and thus would not work..? </p> <p>A few other things have been tried and discarded, but I'm leaving them off the table for now.</p> <p>I hope that, combined with the code below is enough to get a few suggestions; I'm all ears. If anything needs clarification, please ask for it. =) </p> <p>I'm actually rather hoping I'm just being daft and missing something obvious here. And I do realise that this entire mess doesn't quite adhere to good coding practice, in Ada or otherwise, but I'm still sort of stuck with it.</p> <h3>wrapper.cpp</h3> <pre class="lang-cpp prettyprint-override"><code>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 &lt; 42; i++) { update_data( i, -i ); handle_new_data(); } } </code></pre> <h3>data_types.ads</h3> <pre class="lang-none prettyprint-override"><code>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; </code></pre> <h3>data_provider.ads</h3> <pre class="lang-none prettyprint-override"><code>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 =&gt; update_data, external =&gt; "update_data" ); -- return address to record containing data function get_address return system.address; pragma export_function ( internal =&gt; get_address, external =&gt; "get_address" ); -- 'dummy' data; this needs to be passed to data_user data : data_t; end data_provider; </code></pre> <h3>data_provider.adb</h3> <pre class="lang-none prettyprint-override"><code>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; </code></pre> <h3>data_user.ads</h3> <pre class="lang-none prettyprint-override"><code>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 =&gt; set_address, external =&gt; "set_address" ); -- use the new data in internal data structure -- SIMPLIFIED from actual use case procedure handle_new_data; pragma export_procedure ( internal =&gt; handle_new_data, external =&gt; "handle_new_data" ); -- 'dummy' data; this needs to be passed from data_provider data : data_t; end data_user; </code></pre> <h3>data_user.adb</h3> <pre class="lang-none prettyprint-override"><code>with system, unchecked_conversion, data_types; use system, data_types; package body data_user is function to_ptr is new unchecked_conversion ( source =&gt; system.address, target =&gt; 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; </code></pre> <h2>TLDR</h2> <p>Multiple shared libraries, written in Ada and externally called from C++, need to access the same data, stored in an Ada record, preferably without copying. How do I do that?</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload