[m-users.] Calling pass-by-value with C FFI with raylib
Zoltan Somogyi
zoltan.somogyi at runbox.com
Fri Jul 14 17:46:27 AEST 2023
On 2023-07-14 08:29 +02:00 CEST, "Sean Charles (emacstheviking)" <objitsu at gmail.com> wrote:
> However, I am now finding myself running into the same issues again and again
> and it revolves around managing colours. The structure that defines a Color is
> this:
>
> // Color, 4 components, R8G8B8A8 (32bit)
> typedef struct Color {
> unsigned char r; // Color red value
> unsigned char g; // Color green value
> unsigned char b; // Color blue value
> unsigned char a; // Color alpha value
> } Color;
When you have a situation like this where what you want to pass
through the Mercury<->C interface is not a value of a builtin type,
but a compound value, you have two choices.
One is to define the type in Mercury, and pass it through to C in its atomic
components. The Mercury code in your email uses this approach. As you
have discovered, it can be a bit tedious, and in cases like this, where the
C code requires a particular organization of the atomic components,
the constant rearrangement between Mercury's organization of those
component and C's different organization leads to significant overhead.
The other approach is to define the type in C, and to export that type
to Mercury using a foreign_type declaration. In this case case, the
Mercury code will itself use the C organization of the atomic components,
so no rearrangement of those components is required when values
(colors in this case) are passed across the Mercury<->C interface.
The downside is that you have to write foreign_procs in C to access
the components of the color type from Mercury, to construct
new colors, and to convert a color name to a color.
> My question then is: Is there a more compact, clean and probably more
> efficient way to handle the passing by value so that I don't have to keep
> breaking something into 4 uint8s, only to then have to recompose them inside
> the C handler?
Which of the two above approaches is best depends on the particulars
of a given situation. You seem not to like the effects of the first approach,
so the obvious thing to do would be to try the second, and see whether
it works better in your use case.
In situations like this, I would ask myself the question: which language
do you expect to need access to the atomic components of the compound
value more, C or Mercury? If Mercury, then define the type of the compound
value in Mercury. If C, the define it in C.
There are two ways to measure "more" though.One measure is:
which language will have *more places in the code* accessing the components?
The other is: which language will contain more *performance-critical* code
accessing the components? If the answers to these two questions
are the same, then the choice is clear. If they differ, then you have to accept
either a loss of performance, or a loss of convenience.
> I am currently experimenting with passing a uint32 and casting it as a
> Color
This is a form of the second approach, but it suffers from a lack of type safety,
as there is no guarantee that the uint32 you pass to a C function that expects
a color is actually a color, and not something else, such as a coordinate.
Zoltan.
More information about the users
mailing list