comp.lang.ada
 help / color / mirror / Atom feed
* Interfacing to C library...
@ 2002-11-01 15:38 Eric G. Miller
  2002-11-01 17:11 ` Robert A Duff
  0 siblings, 1 reply; 12+ messages in thread
From: Eric G. Miller @ 2002-11-01 15:38 UTC (permalink / raw)



I'm working on building an interface to a C library where most of the
functions are defined as returning a long integer containing the various
error codes returned by each function and data is passed back from the
"functions" via pointer arguments to [usually] structures.  Below is
something of a demonstration of what I've come up with, but I'd appreciate
pointers to better approaches.  Eventually, I'd like to hide the access
type and handle the error codes another way (exceptions maybe...).


/************************* "cfuncs.h" **************************/
#ifndef CFUNCS_H
#define CFUNCS_H

/* error codes, might be multiples via |= */
#define NO_PROBLEMS       0x0000
#define DID_NOT_WORK      0x0001
#define FAILED_MISERABLY  0x0002
#define NASAL_DEMONS      0x0004

typedef struct scores_structure {
	long wins;
	long losses;
} Score_Type;

typedef enum game_names_enum {
	Pin_the_Tail_on_the_Donkey,
	Hop_Scotch,
	Dodge_Ball,
	Television_Tag,
	Kill_the_Man
} Game_Name_Type;

typedef struct game_play_structure {
	Game_Name_Type game;
	Score_Type     scores;
} Game_Type;


long Initialize_Game (Game_Type *Game      /* (in out) The game keeper */
		     ,Game_Name_Type Type  /* (in)     The game type   */
		);

long Play_Game (Game_Type *Game);  /* (in out) Plays the game */

#endif
/*****************************************************************/
\f
/*************************** "cfuncs.c" **************************/
#include <stdlib.h>
#include <time.h>
#include "cfuncs.h"

long Initialize_Game (Game_Type *Game      /* (out) The game keeper */
		     ,Game_Name_Type Type  /* (in)     The game type   */
		)
{
    long status = NO_PROBLEMS;

    if (!Game)
    {
        status |= NASAL_DEMONS;
    }
    else if (Type < Pin_the_Tail_on_the_Donkey || Type > Kill_the_Man)
    {
	status |= FAILED_MISERABLY;
    }
    else
    {
	srand (time(NULL));
	Game->game = Type;
	Game->scores.wins = 0;
	Game->scores.losses = 0;
    }
    return status;
}
	

long Play_Game (Game_Type *Game)  /* (in out) Plays the game */
{
    long status = NO_PROBLEMS;

    if (!Game)
    {
	status |= NASAL_DEMONS;
    }
    else
    {
	int number = rand();
	number >>= 4;
	if (number & 1)
	    Game->scores.wins++;
	else
	    Game->scores.losses++;
    }
    return status;
}
/*****************************************************************/
\f
-- cfuncs.ads
--
-- Interface to cfuncs "game"

package c_funcs is
    -- Error Type
    type Error_Code is mod 2**32;
    -- Error codes
    No_Problems      : constant Error_Code := 16#0000#;
    Did_Not_Work     : constant Error_Code := 16#0001#;
    Failed_Miserably : constant Error_Code := 16#0002#;
    Nasal_Demons     : constant Error_Code := 16#0004#;
    
    type Game_Name_Type is (
            Pin_the_Tail_on_the_Donkey,
            Hop_Scotch,
            Dodge_Ball,
            Television_Tag,
            Kill_the_Man
            );

    pragma Convention (Convention => C,
                       Entity     => Game_Name_Type);

    type Score_Type is
        record
            Wins   : Long_Integer;
            Losses : Long_Integer;
        end record;
        
    pragma Convention (Convention => C,
                       Entity     => Score_Type);
                   
    type Game_Type is
        record
            Game   : Game_Name_Type;
            Scores : Score_Type;
        end record;

    type Game_Type_Ptr is access all Game_Type;

    pragma Convention (Convention => C,
                       Entity     => Game_Type);
                   
    function Initialize_Game (
        Game  : Game_Type_Ptr;
        Name  : Game_Name_Type
        ) return Error_Code;

    function Play_Game (
        Game  : Game_Type_Ptr
        ) return Error_Code;

    pragma Import (Convention    => C,
                   Entity        => Initialize_Game,
                   External_Name => "Initialize_Game");
    
    pragma Import (Convention    => C,
                   Entity        => Play_Game,
                   External_Name => "Play_Game");
                   
end c_funcs;
\f
-- funky_test.adb
with Ada.Text_Io; use Ada.Text_Io;
with C_Funcs; use C_Funcs;

procedure Funky_Test 
is
    Game  : Game_Type_Ptr;
    Error : Error_Code;

    procedure Check_Errors (Error : Error_Code)
    is
    begin
    	if (Error and Did_Not_Work) /= 0 then
	    Put_Line ("Got 'Did_Not_Work' Error!");
	end if;
	if (Error and Failed_Miserably) /= 0 then
	    Put_Line ("Got 'Failed_Miserably' Error!");
	end if;
	if (Error and Nasal_Demons) /= 0 then
	    Put_Line ("Got 'Nasal_Demons' Error!");
	end if;
	if Error /= No_Problems then
	    raise Program_Error;
	end if;
    end Check_Errors;
    
    procedure Print_Scores (Game : in Game_Type_Ptr)
    is
    begin
    	Put_Line (Game_Name_Type'Image(Game.Game) & 
	     ": Won "  & Long_Integer'Image(Game.Scores.Wins) &
	     ": Lost " & Long_Integer'Image(Game.Scores.Losses));
    end Print_Scores;
    
begin
    Game := new Game_Type;
    Error := Initialize_Game (Game => Game, Name => Pin_the_Tail_on_the_Donkey);
    Check_Errors (Error);
    Error := Play_Game (Game);
    Check_Errors (Error);
    Error := Play_Game (Game);
    Check_Errors (Error);
    Error := Play_Game (Game);
    Check_Errors (Error);
    Error := Play_Game (Game);
    Check_Errors (Error);
    Error := Play_Game (Game);
    Check_Errors (Error);
    Error := Play_Game (Game);
    Check_Errors (Error);
    Error := Play_Game (Game);
    Check_Errors (Error);
    Print_Scores (Game);

    Error := Initialize_Game (Game => Game, Name => Dodge_Ball);
    Check_Errors (Error);
    Error := Play_Game (Game);
    Check_Errors (Error);
    Error := Play_Game (Game);
    Check_Errors (Error);
    Error := Play_Game (Game);
    Check_Errors (Error);
    Error := Play_Game (Game);
    Check_Errors (Error);
    Print_Scores (Game);

end Funky_Test;





^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Interfacing to C library...
  2002-11-01 15:38 Interfacing to C library Eric G. Miller
@ 2002-11-01 17:11 ` Robert A Duff
  2002-11-01 17:40   ` tmoran
  0 siblings, 1 reply; 12+ messages in thread
From: Robert A Duff @ 2002-11-01 17:11 UTC (permalink / raw)


"Eric G. Miller" <egm2@jps-nospam.net> writes:

> I'm working on building an interface to a C library where most of the
> functions are defined as returning a long integer containing the various
> error codes returned by each function and data is passed back from the
> "functions" via pointer arguments to [usually] structures.  Below is
> something of a demonstration of what I've come up with, but I'd appreciate
> pointers to better approaches.  Eventually, I'd like to hide the access
> type and handle the error codes another way (exceptions maybe...).

First write a "thin" interface, which is as close to the C code as
possible.  E.g., use a modular type for your error codes, as you did.

Then write another package that is "thicker", and takes advantage
of Ada features like exceptions.

- Bob



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Interfacing to C library...
  2002-11-01 17:11 ` Robert A Duff
@ 2002-11-01 17:40   ` tmoran
  2002-11-02  5:10     ` Eric G. Miller
  0 siblings, 1 reply; 12+ messages in thread
From: tmoran @ 2002-11-01 17:40 UTC (permalink / raw)


> > I'm working on building an interface to a C library where most of the
  The Windows bindings available at www.adapower.com do a lot of this.

> Then write another package that is "thicker", and takes advantage
> of Ada features like exceptions.
  For an example of a thick binding, may I modestly suggest you look
at the source code for Claw available at www.adapower.com
  The TriAda paper available at
www.rrsoftware.com/html/prodinf/triadapaper/triada.html
has a discussion of some the decisions to be made for a thick, Ada-style,
binding.  Also, IEEE Std 1003.5-1992, on the Ada binding to POSIX,
also discusses many of these issues.



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Interfacing to C library...
  2002-11-01 17:40   ` tmoran
@ 2002-11-02  5:10     ` Eric G. Miller
  2002-11-02  6:02       ` tmoran
  2002-11-02 16:10       ` Robert A Duff
  0 siblings, 2 replies; 12+ messages in thread
From: Eric G. Miller @ 2002-11-02  5:10 UTC (permalink / raw)


In <n0zw9.2139$bG.200@rwcrnsc53>, tmora wrote:

>> > I'm working on building an interface to a C library where most of the
>   The Windows bindings available at www.adapower.com do a lot of this.
> 
>> Then write another package that is "thicker", and takes advantage
>> of Ada features like exceptions.
>   For an example of a thick binding, may I modestly suggest you look
> at the source code for Claw available at www.adapower.com
>   The TriAda paper available at
> www.rrsoftware.com/html/prodinf/triadapaper/triada.html
> has a discussion of some the decisions to be made for a thick, Ada-style,
> binding.  Also, IEEE Std 1003.5-1992, on the Ada binding to POSIX,
> also discusses many of these issues.

Okay, how to handle:

/* C header */
struct foo { ... }

long pass_by_value (struct foo); 


-- Ada Spec
type foo is record ... end record;
pragma Convention (C, foo);

function Pass_By_Value (bar : foo) return Long_Integer;
pragma Import (C, Pass_By_Value, "pass_by_value");


Seems Ada always passes records via C pointers. So, the above
doesn't work. Surely, there's a way!  Or do I need a C wrapper for
my Ada wrapper ;-(





^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Interfacing to C library...
  2002-11-02  5:10     ` Eric G. Miller
@ 2002-11-02  6:02       ` tmoran
  2002-11-02 16:10       ` Robert A Duff
  1 sibling, 0 replies; 12+ messages in thread
From: tmoran @ 2002-11-02  6:02 UTC (permalink / raw)


> struct foo { ... }
> long pass_by_value (struct foo);
  Most of the time structs are passed by copy they are probably small (it
being usually unreasonable to copy large objects).  So an
Unchecked_Conversion to Interfaces.C.Int, or Interfaces.C.Double, will do
what you want.
  If not, and you are using Gnat, look up Pragma C_Pass_By_Copy in the
gnat_rm.html document.  For other Ada compilers, look in their docs.



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Interfacing to C library...
  2002-11-02  5:10     ` Eric G. Miller
  2002-11-02  6:02       ` tmoran
@ 2002-11-02 16:10       ` Robert A Duff
  2002-11-02 18:35         ` Eric G. Miller
  1 sibling, 1 reply; 12+ messages in thread
From: Robert A Duff @ 2002-11-02 16:10 UTC (permalink / raw)


"Eric G. Miller" <egm2@jps-nospam.net> writes:

> Seems Ada always passes records via C pointers. So, the above
> doesn't work. Surely, there's a way!  Or do I need a C wrapper for
> my Ada wrapper ;-(

This is a bug in the Ada 95 language, which, I must admit, is partly my
fault.  It has since been "fixed" by adding pragma C_Pass_By_Copy.
See AI-131.  This AI is approved by WG9, and is part of Corrigendum 1.

Perhaps "fixed" is an overstatement.  The bug has been patched with a
kludgy pragma.

- Bob



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Interfacing to C library...
  2002-11-02 16:10       ` Robert A Duff
@ 2002-11-02 18:35         ` Eric G. Miller
  2002-11-02 18:55           ` Robert A Duff
  0 siblings, 1 reply; 12+ messages in thread
From: Eric G. Miller @ 2002-11-02 18:35 UTC (permalink / raw)


In <wcck7jwuf3v.fsf@shell01.TheWorld.com>, Robert A Duff wrote:

> "Eric G. Miller" <egm2@jps-nospam.net> writes:
> 
>> Seems Ada always passes records via C pointers. So, the above
>> doesn't work. Surely, there's a way!  Or do I need a C wrapper for
>> my Ada wrapper ;-(
> 
> This is a bug in the Ada 95 language, which, I must admit, is partly my
> fault.  It has since been "fixed" by adding pragma C_Pass_By_Copy.
> See AI-131.  This AI is approved by WG9, and is part of Corrigendum 1.
> 
> Perhaps "fixed" is an overstatement.  The bug has been patched with a
> kludgy pragma.

Missed that addition since I was looking at a copy of original RM...

Anyway, I don't think the C_Pass_By_Copy semantics quite work the way I
want them too (or, it's "kludgy").  All the "Set" routines of this C
library take C structs by copy while all the "Get" routines take pointers
and all routines return (in the function sense) a long integer status
code.  I've found if I use access types for the "Get" routines, then I 
have to worry about aliasing and need to use T'Unchecked_Access or I
need to dynamically create the objects via "new".  I think I like the
GNAT Import_Function()/Import_Procedure() pragmas a little better, 
since they facilitate finer control over the parameter passing method.
However, I'd rather not use compiler specific capabilities... 




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Interfacing to C library...
  2002-11-02 18:35         ` Eric G. Miller
@ 2002-11-02 18:55           ` Robert A Duff
  2002-11-02 23:59             ` Eric G. Miller
  2002-11-03  9:28             ` Dale Stanbrough
  0 siblings, 2 replies; 12+ messages in thread
From: Robert A Duff @ 2002-11-02 18:55 UTC (permalink / raw)


"Eric G. Miller" <egm2@jps-nospam.net> writes:

> Missed that addition since I was looking at a copy of original RM...

The version with all the Corrigendum AI's included is available on the
net somewhere.  Maybe somebody can post the URL?

> Anyway, I don't think the C_Pass_By_Copy semantics quite work the way I
> want them too (or, it's "kludgy").  All the "Set" routines of this C
> library take C structs by copy while all the "Get" routines take pointers
> and all routines return (in the function sense) a long integer status
> code.

Don't you mean the other way around?  I mean if you're Set-ing a field,
you would need a pointer in C.

>...  I've found if I use access types for the "Get" routines, then I 
> have to worry about aliasing and need to use T'Unchecked_Access or I
> need to dynamically create the objects via "new".

You can use access parameters instead of named access types,
and then you can say 'Access.  Either way, you better make sure the C
code doesn't squirrel away a copy of the pointer.

You can also pass a System.Address to the C routine, and use 'Address.
That's not *so* bad, if you encapsulate it inside a higher-level layer
that simply takes an 'in out' parameter, and uses exceptions to indicate
errors.

>...I think I like the
> GNAT Import_Function()/Import_Procedure() pragmas a little better, 
> since they facilitate finer control over the parameter passing method.
> However, I'd rather not use compiler specific capabilities... 

Good idea.  (At least *I* think portability to non-GNAT compilers is
good, in part because I work for SofCheck, which sells non-GNAT Ada
compilers.  ;-))

- Bob



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Interfacing to C library...
  2002-11-02 18:55           ` Robert A Duff
@ 2002-11-02 23:59             ` Eric G. Miller
  2002-11-03 16:47               ` Robert A Duff
  2002-11-03  9:28             ` Dale Stanbrough
  1 sibling, 1 reply; 12+ messages in thread
From: Eric G. Miller @ 2002-11-02 23:59 UTC (permalink / raw)


In <wccfzujvm21.fsf@shell01.TheWorld.com>, Robert A Duff wrote:

> "Eric G. Miller" <egm2@jps-nospam.net> writes:
> 
>> Missed that addition since I was looking at a copy of original RM...
> 
> The version with all the Corrigendum AI's included is available on the
> net somewhere.  Maybe somebody can post the URL?

adaic has the new one, adahome has the old one...

>> Anyway, I don't think the C_Pass_By_Copy semantics quite work the way I
>> want them too (or, it's "kludgy").  All the "Set" routines of this C
>> library take C structs by copy while all the "Get" routines take pointers
>> and all routines return (in the function sense) a long integer status
>> code.
> 
> Don't you mean the other way around?  I mean if you're Set-ing a field,
> you would need a pointer in C.

You'd think! But it's Set-ing/Get-ing data managed by the library (in 
static C variables).  

>>...  I've found if I use access types for the "Get" routines, then I 
>> have to worry about aliasing and need to use T'Unchecked_Access or I
>> need to dynamically create the objects via "new".
> 
> You can use access parameters instead of named access types,
> and then you can say 'Access.  Either way, you better make sure the C
> code doesn't squirrel away a copy of the pointer.

The library copies data back and forth.  Presumably the "Set" routines
don't take pointers so they can't modify the caller's input structures.
The Get routines just copy data into supplied structs via the pointers.

I'm not sure I understand your statement about using access parameters
versus named access types (I'm still pretty green w.r.t. to Ada)....

Aha! I missed that you couldn't mix "in/out" w/ "access" in a subprogram
parameter list declaration/definition. Okay, definitely nicer than defining
40 access to record types...
  





^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Interfacing to C library...
  2002-11-02 18:55           ` Robert A Duff
  2002-11-02 23:59             ` Eric G. Miller
@ 2002-11-03  9:28             ` Dale Stanbrough
  1 sibling, 0 replies; 12+ messages in thread
From: Dale Stanbrough @ 2002-11-03  9:28 UTC (permalink / raw)


Robert A Duff wrote:

> >...I think I like the
> > GNAT Import_Function()/Import_Procedure() pragmas a little better, 
> > since they facilitate finer control over the parameter passing method.
> > However, I'd rather not use compiler specific capabilities... 
> 
> Good idea.  (At least *I* think portability to non-GNAT compilers is
> good, in part because I work for SofCheck, which sells non-GNAT Ada
> compilers.  ;-))

How do other compiler companies deal with this issue? My guess is that
a lot of code is written with Gnat, and very little of it is compiled
with the "no gnat pragmas" option set.

Is there a trend to incldue Gnat pragmas in all other compilers?

Dale



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Interfacing to C library...
  2002-11-02 23:59             ` Eric G. Miller
@ 2002-11-03 16:47               ` Robert A Duff
  2002-11-03 18:55                 ` Eric G. Miller
  0 siblings, 1 reply; 12+ messages in thread
From: Robert A Duff @ 2002-11-03 16:47 UTC (permalink / raw)


"Eric G. Miller" <egm2@jps-nospam.net> writes:

> adaic has the new one, adahome has the old one...

Thank you.  I printed out the new version of the AARM (on a double-sided
laser printer!) for my own use a while back, but I had forgotten where I
got it.

> I'm not sure I understand your statement about using access parameters
> versus named access types (I'm still pretty green w.r.t. to Ada)....

Probably because I don't really understand what you're trying to do.

Anyway, I'll try to explain:

    type R is record....
    type A is access all R;

    procedure P(X: A; Y: access R);

A is a "named access type".
X is a "parameter of a named access type".
Y is an "access parameter", which is the same thing as
"a parameter of an anonymous access type".

There are various subtle differences between named and anonymous access
types, which I won't explain in detail.  One important difference is
that access parameters allow dispatching (assume R is tagged),
whereas parameter of a named access type do not.

The accessibility rules are different, which is what allows you to say
'Access in some cases with access parameters, where otherwise
'Unchecked_Access would be required.

> Aha! I missed that you couldn't mix "in/out" w/ "access" in a subprogram
> parameter list declaration/definition. Okay, definitely nicer than defining
> 40 access to record types...

You *can* mix 'in out' with 'access':

    procedure P(X: in out R; Y: access R);

is legal.  Perhaps what you're thinking of is the fact that functions do
not allow 'in out' parameters, so sometimes people use access parameters
instead, when they want a function to have side effects on its
parameters.  C functions do that all the time -- e.g., you pass a
"char *", and the function writes upon the thing pointed-to, and *also*
returns some sort of status code.

It seems to me that on the C side, in your Get/Set example, you have
struct parameters passed by copy, and pointer-to-struct parameters.  On
the Ada side, the former can correspond to an 'in' mode parameter of a
record type, and the latter can correspond to an access parameter,
or to a parameter of a named access type, or to a parameter of type
System.Address.

I really think the best advice in writing bindings is to look at some
examples.  Somebody suggested CLAW, which is a good example.  The people
who wrote it have much more experience in this area than I do.  If you
don't understand some detail, ask about it here.

- Bob



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Interfacing to C library...
  2002-11-03 16:47               ` Robert A Duff
@ 2002-11-03 18:55                 ` Eric G. Miller
  0 siblings, 0 replies; 12+ messages in thread
From: Eric G. Miller @ 2002-11-03 18:55 UTC (permalink / raw)


In <wccpttm1txo.fsf@shell01.TheWorld.com>, Robert A Duff wrote:

> "Eric G. Miller" <egm2@jps-nospam.net> writes:

>> Aha! I missed that you couldn't mix "in/out" w/ "access" in a subprogram
>> parameter list declaration/definition. Okay, definitely nicer than defining
>> 40 access to record types...
> 
> You *can* mix 'in out' with 'access':
> 
>     procedure P(X: in out R; Y: access R);
> 
> is legal.  Perhaps what you're thinking of is the fact that functions do
> not allow 'in out' parameters, so sometimes people use access parameters
> instead, when they want a function to have side effects on its
> parameters.  C functions do that all the time -- e.g., you pass a
> "char *", and the function writes upon the thing pointed-to, and *also*
> returns some sort of status code.

I was thinking of 6.1.15
                                                                                                      
   parameter_specification ::=                                                                             
       defining_identifier_list : mode  subtype_mark [:= default_expression]                               
     | defining_identifier_list : access_definition [:= default_expression] 

procedure Foo (Thing : in out access Bar);

But, I suppose that doesn't make any sense...  Anyway, thanks for the
help...




^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2002-11-03 18:55 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-11-01 15:38 Interfacing to C library Eric G. Miller
2002-11-01 17:11 ` Robert A Duff
2002-11-01 17:40   ` tmoran
2002-11-02  5:10     ` Eric G. Miller
2002-11-02  6:02       ` tmoran
2002-11-02 16:10       ` Robert A Duff
2002-11-02 18:35         ` Eric G. Miller
2002-11-02 18:55           ` Robert A Duff
2002-11-02 23:59             ` Eric G. Miller
2002-11-03 16:47               ` Robert A Duff
2002-11-03 18:55                 ` Eric G. Miller
2002-11-03  9:28             ` Dale Stanbrough

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox