Question:
How do I access a chunck of memory as an Ada string with out
moving or copying the data in Ada95?
Answer:
Well we are not going to be able to find a
solution that is *formally* portable for something
like this, but we can certainly find a solution
that is from a pragmatic point of view portable
(between architectures and between compilers).
There are a couple of approaches. First, let's
try to get a type that retains proper bounds
checking:
First, we want to deal with a constrained array
here. Unconstrained arrays have bounds stored
in some implementation dependent format and will get
you into trouble. So let's first define a subtype
that appropriately represents the range
subtype XString is String (1 .. expr);
Here expr can be dynamic, there is no problem in
that. However, note that we must define this
subtype in an appropriate scope. In this
particular case, expr is something like
Positive
(System.Storage_Elements.To_Integer (B) -
System.Storage_Elements.To_Integer (A) + 1);
Now we can proceed in several ways
Method 1a. Simply use an address clause. This is
exactly a legitimate use of address clauses, so we
simply say
S : XString;
for S'Address use A;
This will likely work on most implementations, assuming that
they do not use dope vectors or descriptors for constrained
arrays. This is a reasonable assumption, and certainly true
of GNAT, but in practice it may be slightly more portable
to use:
Method 1b. Use Unchecked_Conversion
type XString_Ptr is access all XString; function
To_XString_Ptr is new Unchecked_Conversion
(Address, XString_Ptr);
SP : XString_Ptr := To_String_Ptr (A);
in practice, this works fine, since all
implementations will use the same representation
for constrained pointers and addresses. Note that
approach 1b will NOT work if you try to use
"access string" instead of the
pointer-to-constrained type, since then bounds get
involved.
However, there is no requirement that this work, so a bit
cleaner, is to use:
Method 1c. Use Address_To_Access_Conversions
package XString_Ops is new
Address_To_Access_Conversions (XString);
subtype Xstring_Ptr is Xstring_Ops.Object_Pointer;
SP : XString_Ptr := To_Pointer (A);
Note: although the formal of this package has a
declaration (<>) implying you can use it with an
unconstrained type such as String, you will get
into trouble if you try, since we still have the
bounds problem.
Basically the issue with "access String" is that
values of this type have bounds. Where are these
bounds stored? Certainly your memory mapped gizmo
has no bounds in sight, and even if it did your
implementation might not have the same idea of how
to store them.
Here are two rules to follow
NEVER use unchecked conversion with pointers to
unconstrained array types
NEVER use Address_To_Access_Conversions with
pointers to unconstrained array types.
Violating these rules is a common cause of
non-portable code and obscure bugs. We find this
coming up frequently in our work helping customers
to port legacy Ada 83 code.
OK, those approaches work fine, but now for one
other approach that is a little more general and
flexible, but does not retain bounds checking.
Define a type
subtype Big_String is String (Positive); type
Big_String_Ptr is access Big_String;
This type acts very much like char* in C in that
it is a pointer that can be used to access
arbitrary sized character arrays.
Method 2b. Like 1b but use Big_String_Ptr
Method 2c. Like 1c but use Big_String_Ptr
The advantage of Method 2b and 2c is that
Big_String_Ptr can be used arround the place
freely without worrying about specialized
definitions of subtypes and their scopes. This
approach is also appropriate when you don't know
the upper bound in advance, but it is computed as
you go along, or changes as you go along.
For an example of the use of "Big" arrays of this
type, see the GNAT.Table package in the GNAT
library, which uses this approach to get the
effect of virtual origin addressing for arrays.
Method 3. Pointer Arithmetic
Finally, yes, you could use address arithmetic
(good old pointer arithmetic), and indeed Ada has
much more flexible pointer arithmetic than C (in
Ada, unlike C, you can do arbitrary address
arithmetic, e.g. compare any two addresses, in a
portable manner). However, this is much lower
level and will kludge up your code unnecessarily.
Ada 83 notes.
In Ada 83, method 1a is a bit more dubious, it may be
erroneous, but in practice if the addressed area is not
otherwise aliased, it should work fine. Method 1b is
certainly safe (same comments apply to 2a and 2b). Method
1c, probably the preferable approach for Ada 95 is of course
not available in Ada 83.
|