Help creating a thick binding (aka, improving Tsoding's Eepers game)

Hi all,

I wanted to fully refactor Tsoding’s Eepers game as I believe it can be greatly improved upon.
One of the first things that I wanted to do, is to decouple the bindings to the Raylib library (written in C) from the main source code of the game. The reasoning behind this is that I want the Ada code to be fully Ada, and the C code to be hidden away. For that reason, I wanted to create a thick binding that would fully hide the C binding from the user.

I started my journey already, which you can see in my thick-binding branch. I have already done the porting (more or less) for the raymath module.

The experience was not great:

  • creating the Ada function specs
  • the C and Ada data-types
  • the To_C (more on that later) and To_Ada functions
  • putting the C functions as internal function of the Ada functions
  • creating the (very simple and repetitive!) bodies of the Ada functions…

I think this can be done better… Here is the thing, I know Ada allows for very simple function definitions to be one liners, see:

function Is_Origin (P : in Point) return Boolean is
   (P.X = 0.0 and P.Y = 0.0);

And I thought that maybe I can simplify all this boilerplate to something easier in the spec file, like:

function My_Ada_Func(Left, Right : Vector2) return Vector2 is
   ((To_C(Left) + To_C(Right));  -- "+" operator is a C import

However, I cannot seem to be able to do it… Why?
Well, the “+” C-function needs to be visible, but I do not want that, I want it as a private function so that the consumer of the library cannot use it. However, I do want the My_Ada_Func to be visible, and I cannot seem to be able to mix these two things together. And a very similar story takes place with the “custom” To_C function. I do not want it to be visible, but it is needed by the Ada function…

Do you have any ideas or recommendations? Can the idea I am proposing here be done? Basically, what I want boils down to:

  • Have potentially only the .ads file with simple function definitions (one liners like the one above)
  • Have all the C stuff hidden away and unable to be used by the library consumers
  • Have it all work together

The Ada standard can be as new as needed.

Thank you for your help,
Fer

P.S.: you can take a look at the horrible code I have already changed in the link provided above.

1 Like

Here’s the thing about binding to C from Ada, it’s a MASSIVE, tedious pain in the arse.

There are 3 ways really:

  1. Thin, uses types from the Interfaces.C.* packages, so you’re writing C in Ada.
  2. Variable thickness, like I do with SDLAda, you hide as much as possible, but you define your own types.
  3. Thick, it’s heavy and one function call can end up being multiple.

I think 2 is the way to go.

  1. Define your own Ada types with C convention and ranges to match those of the C counterpart,
  2. If a function returns void and takes void, don’t wrap it, change the name.
  3. If the C API defines it’s own boolean type and returns it, wrap those functions in inline functions, i.e. return thing = Custom_True;, plenty of these in SDL.
  4. Sometimes, macros need to be translated, if they can expression functions, use them.
  5. Use overloading, as you mention for "+" above, which will be some Add() function.

3 is likely the solution to what you want here especially if visibility of C stuff is not wanted.

1 Like

Avoid using the abbreviated function bodies in those cases.

As for Lucretia’s suggestion, I usually write a thin binding followed by a thick binding which uses it. My UDP libraries would be examples of this approach. Unless the library is utterly trivial, splitting the work in this way causes no real issues and yet makes it more approachable.

You can forward declare the function in the public part and do the one liner in the private part:

   function My_Ada_Func(Left, Right : Vector2) return Vector2;

private

   -- "+" function defined somewhere here
   -- Same with To_C

   function My_Ada_Func(Left, Right : Vector2) return Vector2 is
      ((To_C(Left) + To_C(Right));  -- "+" operator is a C import

Subprogram completions can be expression functions.

4 Likes

Yeah, I remembered this point after I’d posted last night.

I’m coming to the realisation that the mixed up thin/thick binding in SDL is becoming problematic, it always was with the OOP bits mixed up in the thing. I think it’s better to build a mixed thickness binding using the techniques in 1-3, but if you want to go further, i.e. controlled types, it really needs to be a new layer on top.

You can see why with all the pointer hiding crap in SDL.

Hi,
just to be sure, did you consider Fabien’s Raylib binding? (available on Github and Alire)
And there is another one, don’t know if it’s yours GitHub - mimo/raylib-Ada: Ada binding for the Raylib library.

Thank you Jere! This seems to do the trick!! I will report back if I can manage to do the full binding with this, but it does seem to do exactly what I need!!!

Hi Lionel!
I knew there was a binding already for Raylib, thank you for pointing that out :slight_smile: But my goal is to refactor eepers code as is, without using external help. I want to document the improvements that could have been done to the code that already exists :slight_smile: I will nonetheless mention them!

Best regards and thank you everybody!
Fer

1 Like