comp.lang.ada
 help / color / mirror / Atom feed
* Passing C-style flags to C subprograms
@ 2012-03-30 18:18 Natasha Kerensikova
  2012-03-30 19:57 ` Randy Brukardt
  2012-03-30 21:55 ` Jeffrey Carter
  0 siblings, 2 replies; 6+ messages in thread
From: Natasha Kerensikova @ 2012-03-30 18:18 UTC (permalink / raw)


Hello,

I have been trying to write an Ada binding to libevent2. There are all
sorts of issues and I have only started, so I'm not sure it will ever
lead to anything interesting, except for my own personal enlightenment.
One of these issues is passing and retrieving "C-style" flags, and I
would love to see sharing of insights on this topic.

I guess "C-style flags" could be defined as a set of boolean values
represented on the least significant of a machine word, and manipulated
as "int" values (or sometimes "unsigned" or "unsigned long").

The pattern in libevent2 is to declare an enum type for the flag set
objects (which allows some form of type checking), and enumeration
elements are powers of two, so that can be or'ed together to form the
final flag integer.

So the problem is, what is the best way to deal with such flags from
Ada?

Of course "best" depends on how a solution is evaluated. Usually my
first priority is client code readability and maintenability, followed
by library code readability and maintanability, followed by
"efficiency". However sometimes (in critical parts identified through
profiling and/or other a-posteriori analysis) time and/or space
efficiency jumps as the first concern, and having a solution for these
cases might be useful too (especially for bindings of stuff like
libevent, which is advertised for its scalability).

I found a solution in Florist. I don't whether it's specific to Florist
or standard (I haven't been able to find a reference for POSIX for Ada).
It defines a private Option_Set type, which is actually a modular
integer, and operations on it implemented as bitwise arithmetic.

Then binding C-style flags looks like this:

   type These_Flags is new POSIX.Option_Set;

   Allow_Stuff, Do_Otherwise, Special_Feature : constant These_Flags;

   procedure Set_These_Flags (Flags : These_Flags);
   pragma Import (C, Set_These_Flags, "set_these_flags");

   function Get_Flags return These_Flags;
   pragma Import (C, Get_Flags, "get_flags");

private
   Allow_Stuff : constant These_Flags := These_Flags (POSIX.Option_1);
   Do_Otherwise : constant These_Flags := These_Flags (POSIX.Option_2);
   Special_Feature : constant These_Flags := These_Flags (POSIX.Option_3);



And then in the client:

   Set_These_Flags (Allow_Stuff + Do_Otherwise);



On the other hand, while experimenting with my bindings, I came up with
a completely different approach, based on record of booleans and
representation clauses, so that it can be passed-by-copy and be
interpreted correctly as an integer.

Here is how it looks like:

   type These_Flags is record
      Allow_Stuff : Boolean := False;
      Do_Otherwise : Boolean := False;
      Special_Feature : Boolean := False;
   end record;
   for These_Flags use record
      Allow_Stuff at 0 range 0 .. 0;
      Do_Otherwise at 0 range 1 .. 1;
      Special_Feature at 0 range 2 .. Interfaces.C.int'Size;
   end record;
   for These_Flags'Size use Interfaces.C.int'Size;
   pragma Convention (C_Pass_By_Copy, These_Flags);

   procedure Set_These_Flags (Flags : These_Flags);
   pragma Import (C, Set_These_Flags, "set_these_flags");

   function Get_Flags return These_Flags;
   pragma Import (C, Get_Flags, "get_flags");

And then

   Set_These_Flags (These_Flags'(Allow_Stuff => True,
                                 Do_Otherwise => True,
                                 Special_Feature => False));


I find the record-based approach feels more "Ada-ish" while Florist
approach feels much more C-ish, it doesn't clutter the namespace, and it
allows for 2-value enumerations that don't map to boolean (e.g.
switching between two different modes depending on the bit value), so
I generally prefer the former. However, the declaration is much bulkier
and uglier and there is no code reuse.

However, a much more pressing question is: does the record-based
approach actually work?

The fact that it run successfully on my machine does not mean it would
run successfully anywhere else. Even worse, it might run but silently
produce incorrect results.

If I understand the RM correctly, it is guaranteed that
Boolean'Pos (False) = 0 and Boolean'Pos (True) = 1.
But does that mean a 1-bit representation of a boolean necessarily match
the position value?
Should I add some representation clauses somewhere to fix that?

Can I trust the extended range of the last flag to not cause any
trouble? (I used it rather than "range 2 .. 2" to avoid a GNAT warning,
and it seemed less ugly than a pragma Warnings or bother the client with
an explicit padding field).

Could endianness break the solution? (I understood that that "at 0" part
avoids it by looking at bit rank in a machine word, so with the same
mapping as a C int, but I might be wrong).

Are there any other issues I have missed?


Thanks in advance for your insights,
Natasha



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

end of thread, other threads:[~2012-04-03  2:14 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-03-30 18:18 Passing C-style flags to C subprograms Natasha Kerensikova
2012-03-30 19:57 ` Randy Brukardt
2012-04-02 10:40   ` Natasha Kerensikova
2012-04-03  2:14     ` Randy Brukardt
2012-04-03  1:44   ` BrianG
2012-03-30 21:55 ` Jeffrey Carter

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