From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-0.9 required=5.0 tests=BAYES_00,FORGED_GMAIL_RCVD, FREEMAIL_FROM autolearn=no autolearn_force=no version=3.4.4 X-Google-Thread: 103376,c9bb37a1fdd0d3e9,start X-Google-NewGroupId: yes X-Google-Attributes: gida07f3367d7,domainid0,public,usenet X-Google-Language: ENGLISH,ASCII-7-bit Received: by 10.68.235.4 with SMTP id ui4mr1205348pbc.3.1333131531242; Fri, 30 Mar 2012 11:18:51 -0700 (PDT) Path: z9ni19512pbe.0!nntp.google.com!news2.google.com!volia.net!news2.volia.net!feed-A.news.volia.net!news.musoftware.de!wum.musoftware.de!news.swapon.de!eternal-september.org!feeder.eternal-september.org!mx04.eternal-september.org!.POSTED!not-for-mail From: Natasha Kerensikova Newsgroups: comp.lang.ada Subject: Passing C-style flags to C subprograms Date: Fri, 30 Mar 2012 18:18:50 +0000 (UTC) Organization: A noiseless patient Spider Message-ID: Mime-Version: 1.0 Injection-Date: Fri, 30 Mar 2012 18:18:50 +0000 (UTC) Injection-Info: mx04.eternal-september.org; posting-host="Mda950WjNwNLAFOE7yJXQw"; logging-data="18064"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18qrAN1ZanZzd83r20Ea9xq" User-Agent: slrn/0.9.9p1 (FreeBSD) Cancel-Lock: sha1:62mX0Sq4sFNHMcbZn5VvAslUs6E= Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Date: 2012-03-30T18:18:50+00:00 List-Id: 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