From: Björn Persson on
resander wrote:

> void getproduct ( PRODUCT * pout , int * resultout )
> {
> *resultout = guidb ( 0 , (int)pout ) ;
> }

Who is forcing you to cast pointers to int? I recommend applying a clue-by-
four to that person's head. That code is broken on 64-bit GNU/Linux. Good
luck porting that program to Windows if what you have shown is
representative of its quality.

Before you write the Ada binding you should fix the interface so that it's
sane according to the C standard.

--
Bj�rn Persson
PGP key A88682FD
From: Keith Thompson on
resander <kresander(a)gmail.com> writes:
> I am working on software that manipulates SQL databases via a GUI. It
> is written in C or C++ without classes and compiled on Linux. Would
> also want it to run on Windows eventually. This software has
> essentially one entry procedure:
>
> int guidb ( int appno , int recaddress ); // in C
>
> which returns an outcome as an int, takes an int appno which specifies
> application and an address of a record in recaddress. The latter is
> used for passing the memory address of any recordtype variable and is
> cast to a byte address inside guidb.
[...]

As others have mentioned, this is bad C.

There is no guarantee that an int can hold the value of an address,
and there are common systems (including Linux systems) where it
can't. Worse, you're not likely to get a warning if you convert
a pointer to int, even if there's a loss of information.

The right type for this parameter is void* (which is roughly
analogous to Ada's System.Address).

--
Keith Thompson (The_Other_Keith) kst-u(a)mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
From: resander on
On Apr 22, 10:12 pm, Keith Thompson <ks...(a)mib.org> wrote:
> resander <kresan...(a)gmail.com> writes:
> > I am working on software that manipulates SQL databases via a GUI. It
> > is written in C or C++ without classes and compiled on Linux. Would
> > also want it to run on Windows eventually. This software has
> > essentially one entry procedure:
>
> >   int guidb ( int appno , int recaddress );  // in C
>
> > which returns an outcome as an int, takes an int appno which specifies
> > application and an address of a record in recaddress. The latter is
> > used for passing the memory address of any recordtype variable and is
> > cast to a byte address inside guidb.
>
> [...]
>
> As others have mentioned, this is bad C.
>
> There is no guarantee that an int can hold the value of an address,
> and there are common systems (including Linux systems) where it
> can't.  Worse, you're not likely to get a warning if you convert
> a pointer to int, even if there's a loss of information.
>
> The right type for this parameter is void* (which is roughly
> analogous to Ada's System.Address).
>
> --
> Keith Thompson (The_Other_Keith) ks...(a)mib.org  <http://www.ghoti.net/~kst>
> Nokia
> "We must do something.  This is something.  Therefore, we must do this."
>     -- Antony Jay and Jonathan Lynn, "Yes Minister"




Thank you all for your concerns about using an int for passing an
address. The interface actually uses a void * (could also be char * as
that is what is needed), but I changed it for the post because I
thought an all-int interface would be easier to handle at the Ada-to-C
boundary. That seems to have backfired!

It may help if I give some background info, so just briefly the
program (with guidb as the main entry routine) moves record data to/
from GUI dialogs and to and from SQL databases via ODBC. A record in
host language may be passed in or in-and-out. The guidb is not
compiled with host language units and would exist as a dll, shared
object or a static library. It uses a record memory address, record
length and field offsets and field types/length in order to access a
record and move the record around. This information is held in tables
local to guidb. The record length and field data are passed accross
via another entry point:

void givefldinfo ( int recordtypeid , -- key
int recordlength ,
int numflds ,
int fldoffsets[] ,
int typeinfo[] );

The givefldinfo is called for each record type by initialisation
routines that are generated automatically from design-time or
repository information. They are compiled and linked with the host
application and called behind the scenes.

Usually GUI events are handled internally, but may also be handled by
event procedures in the host language. Host language handlers have
signatures ( bool valin ),( int valin ), ( char valin[] ) and
( RECORDTP * valinout ). There are four additional entry points, one
for each event handler signature to get the proc address across from
host language. These are called in a manner similar to givefldinfo.

I am on Linux and installed Ada GPS yesterday. Experimented a bit and
found that X.fld'Position gave me the field offsets and X'Size the
record size. I also tried getting the address of a record and its
fields using X'Address and X.fld'Address, but they all came out
negative.

Prodrecord address: -1079352648
code field address: -1079352648
bcode field address: -1079352644
ccode field address: -1079352643
third field address: -1079352640
descr field address: -1079352636
price field address: -1079352624

for record
code : integer; bcode : character;
ccode : character; third : integer;
descr : String(1..10); price : float;
by:
prod : PRODUCT;
a: System.Address;
function Conv is new Ada.Unchecked_Conversion (
Source => System.Address,
Target => Integer);
...
a := prod'Address;
put( "Prodrecord address: " );
s := conv ( a ) ;
put(s);
new_line;

a := prod.code'Address;
put( " code field address: " );
s := conv ( a ) ;
put(s);
new_lin
...
Q1.
Addresses are not negative. How do I output a System.Address?


Dmitri:
Many thanks for showing how to code the generic Get routine. I have
never used generics or interfaced to C before and it was so long time
ago I used Ada (where is my walking stick?) so I don't understand it
well enough.

Q2.
What is the effect of aliased in 'Data : aliased Data_Type;'?

Data is a local variable. I am confused by the aliased keyword, but it
would seem guidb copies the record into this and then the record value
is copied once more into final location (X:Product). If so, one copy
operation is redundant.

Q3.
The wrapper:
procedure getprod (p:out PRODUCT;res:out int) is
begin
result = guidb ( 0 , addrin(p)??? ) ;
end

hides the details of calling the C function and returns data to the
record variable in the calling program if the address carried by the
formal parameter can be obtained inside the wrapper. Using p'Address
for the second actual parameter of guidb would probably just give the
address of the formal (will check when I know how to output a
System.Address).
Is it possible to obtain the memory address of the record variable via
formal parameter p?

Q4.
Would it be easier to implement this if the wrapper was changed to:

procedure getprod (p: in System.Address; res:out int) is
begin
result = guidb ( 0 , what goes here??? ) ;
end

with user calling it as:

getprod ( recordvar'Address , result );


Q5.
Host language event handler procedures are stored as an array of
procedure addresses which is local to guidb. A procedure is called
indirectly via this array. This was designed for C/C++, but it would
be nice to use the same or similar mechanism for Ada.
Is this going to be possible?


I think interfacing to Ada can be done, but my knowledge about this is
still very limited. Your help would be most appreciated.

Regards
Ken

From: Dmitry A. Kazakov on
On Fri, 23 Apr 2010 05:58:05 -0700 (PDT), resander wrote:

> It may help if I give some background info, so just briefly the
> program (with guidb as the main entry routine) moves record data to/
> from GUI dialogs and to and from SQL databases via ODBC.

There is GNADE ODBC, Ada bindings to ODBC. So you can drop C/C++
altogether.

> for record
> code : integer; bcode : character;
> ccode : character; third : integer;
> descr : String(1..10); price : float;
> by:
> prod : PRODUCT;
> a: System.Address;
> function Conv is new Ada.Unchecked_Conversion (
> Source => System.Address,
> Target => Integer);
> ...
> a := prod'Address;
> put( "Prodrecord address: " );
> s := conv ( a ) ;
> put(s);
> new_line;
>
> a := prod.code'Address;
> put( " code field address: " );
> s := conv ( a ) ;
> put(s);
> new_lin
> ...
> Q1.
> Addresses are not negative.

Never use Unchecked_Conversion unless you exactly know what you are
doing... Address is modular, when bit-wise converted to a *signed* integer
the result is expectedly surprising.

> How do I output a System.Address?

The example above in legal Ada:

with Ada.Text_IO; use Ada.Text_IO;
with System.Storage_Elements; use System.Storage_Elements;
with Interfaces.C; use Interfaces.C;

procedure Test_Fields is
type Product is record
Code : int; -- Use only C-compatible types when you communicate
Bcode : char; -- to C
Ccode : char;
Third : int;
Descr : char_array (1..10);
Price : c_float;
end record;
pragma Convention (C, Product);
P : aliased Product;
begin
Put_Line ("P'Address:" & Integer_Address'Image (To_Integer
(P'Address)));
Put_Line ("P.Code'Address:" & Integer_Address'Image (To_Integer
(P.Code'Address)));
Put_Line ("P.Descr'Address:" & Integer_Address'Image (To_Integer
(P.Descr'Address)));
end Test_Fields;

gives:

P'Address: 38207168
P.Code'Address: 38207168
P.Descr'Address: 38207180

> Dmitri:
> Many thanks for showing how to code the generic Get routine. I have
> never used generics or interfaced to C before and it was so long time
> ago I used Ada (where is my walking stick?) so I don't understand it
> well enough.

Ada generic is roughly what C++ calls template.

> Q2.
> What is the effect of aliased in 'Data : aliased Data_Type;'?
>
> Data is a local variable. I am confused by the aliased keyword, but it
> would seem guidb copies the record into this and then the record value
> is copied once more into final location (X:Product). If so, one copy
> operation is redundant.

"aliased" means "dear compiler, I plan to take pointers (access type) to
this thing." The compiler will reject Data'Access otherwise, because
without your hint it is free to allocate it in a way that Data would have
no valid machine address at all.

> Q3.
> The wrapper:
> procedure getprod (p:out PRODUCT;res:out int) is
> begin
> result = guidb ( 0 , addrin(p)??? ) ;
> end
>
> hides the details of calling the C function and returns data to the
> record variable in the calling program if the address carried by the
> formal parameter can be obtained inside the wrapper. Using p'Address
> for the second actual parameter of guidb would probably just give the
> address of the formal (will check when I know how to output a
> System.Address).
> Is it possible to obtain the memory address of the record variable via
> formal parameter p?

In general case no, because see above. However if you declare PRODUCT a
"limited type" it will be passed by reference. You also [worse] can

procedure getprod (p: access PRODUCT; ...);

BTW, your concern about copying records is absolutely ungrounded.
Especially because you are using ODBC + a relational DBMS! These are so
extraordinary slow, that you will note no difference even if you did
hundreds of copies.

> Q4.
> Would it be easier to implement this if the wrapper was changed to:
>
> procedure getprod (p: in System.Address; res:out int) is
> begin
> result = guidb ( 0 , what goes here??? ) ;
> end
>
> with user calling it as:
>
> getprod ( recordvar'Address , result );

The preferences are as follows:

in out >> access >>>>>>>>>>> address

> Q5.
> Host language event handler procedures are stored as an array of
> procedure addresses which is local to guidb. A procedure is called
> indirectly via this array. This was designed for C/C++, but it would
> be nice to use the same or similar mechanism for Ada.
> Is this going to be possible?

Yes, it is possible, you can have an array of access-to-procedure type. But
an OO way is preferable.

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: John B. Matthews on
In article
<e98bcf01-46cc-44dd-b57b-3cc7e273d9e2(a)j21g2000yqh.googlegroups.com>,
resander <kresander(a)gmail.com> wrote:

> Prodrecord address: -1079352648
> code field address: -1079352648
> bcode field address: -1079352644
> ccode field address: -1079352643
> third field address: -1079352640
> descr field address: -1079352636
> price field address: -1079352624
> ...
> Q1.
> Addresses are not negative. How do I output a System.Address?

-1079352648 = 0xBFAA62B8
-1079352644 = 0xBFAA62BC
....

This recent thread discusses several ways to display hexadecimal:

<http://groups.google.com/group/comp.lang.ada/browse_frm/thread/f3514db0a21f9b44>

A.10.8 Input-Output for Integer Types may be the most straightforward:

<http://www.adaic.com/standards/05rm/html/RM-A-10-8.html>

--
John B. Matthews
trashgod at gmail dot com
<http://sites.google.com/site/drjohnbmatthews>