Skip to content

namgyaaal/libffi_experimental

Repository files navigation

About

Dyalog APL 20.0's APLAN/⎕VGET allows for a unique way of handling foreign library calling.

This repo is dedicated to (i) learning libffi through a rust implementation of a wrapper over it in src/lib.rs and (ii) imagining what a foreign library caller would look like utilizing APLAN.

This is highly experimental and nothing is guaranteed, I have not guaranteed edge cases working, since this for prototyping. Because of this, I don't recommend using this for your projects.

Get started

This requires Dyalog 20.0, the latest cargo, CMake and a C compiler used by CMake.

It can be tested as:

./compile.sh
./test.apls

Note that it currently only runs on MacOS and Linux.

Reasoning

Consider this struct definition in SDL3 GPU:

typedef struct SDL_GPUColorTargetInfo
{
    SDL_GPUTexture *texture;
    Uint32 mip_level;
    Uint32 layer_or_depth_plane;
    SDL_FColor clear_color;
    SDL_GPULoadOp load_op;
    SDL_GPUStoreOp store_op;
    SDL_GPUTexture *resolve_texture;
    Uint32 resolve_mip_level;
    Uint32 resolve_layer;
    bool cycle;
    bool cycle_resolve_texture;
    Uint8 padding1;
    Uint8 padding2;
} SDL_GPUColorTargetInfo;

When this library is used normally, the struct can be defined like this with default initialization:

SDL_GPUColorTargetInfo colorTargetInfo{};
colorTargetInfo.clear_color = {255/255.0f, 219/255.0f, 187/255.0f, 255/255.0f};
colorTargetInfo.load_op = SDL_GPU_LOADOP_CLEAR; 
colorTargetInfo.store_op = SDL_GPU_STOREOP_STORE;
colorTargetInfo.texture = texture; 

While with ⎕NA it would currently have to account for all struct elements when calling a function that takes this struct (ignoring any possible alignment issues).

In my project, I have to handle it like this:

color_info ← ⊂(swap_texture 0 0 (0.1 0.2 0.3 1) 1 0 0 0 0 0 0 0 0)

pass ← lagl.SDL_BeginGPURenderPass cmd_buf (color_info) 1 (depth_info)

If we have an idea of what the types are supposed to be and what functions take, a wrapper can be made where it could look something like this instead with APLAN:

color_info ← (
    texture: swap_texture
    clear_color: (r: 0.1 ⋄ g: 0.2 ⋄ b: 0.3 ⋄ a: 1)
    load_op: 1
)

pass ← lagl.SDL_BeginGPURenderPass cmd_buf (color_info) 1 (depth_info)

Where the function instead is a auto-generated wrapper one over the raw one that unpacks the namespace in order, utilizing ⎕VGET to set defaults and reading out namespaces to call the function with.

This repo is testing to see if this is possible.

Example

Basic

typedef struct Inner {
    uint32_t a;
    uint32_t b;
} Inner;

uint32_t fn_struct(Inner s) { return s.a + s.b; }

Can be currently done as:

⎕SE.Link.Create 'ffiw' './ffiw'
⎕IO ← 0

inner ← ffiw.Build_Struct ['a' ffiw.types.u32 ⋄ 'b' ffiw.types.u32]
⎕FX ffiw.Build_Function 'fn_struct' ['s' inner⋄] ffiw.types.u32
⎕ ← 'fn_struct', fn_struct(s: (a: 1 ⋄ b: 2))

Default Initializations

With namespaces and ⎕VGET, functions such as

typedef struct Inner {
    uint32_t a;
    uint32_t b;
} Inner;

typedef struct Outer {
    Inner a;
    Inner b;
} Outer;

uint32_t fn_c(Outer a) { return a.a.a + a.a.b + a.b.a + a.b.b; }

can be called in APL like this:

inner ← ffiw.Build_Struct ['a' ffiw.types.u32 ⋄ 'b' ffiw.types.u32]
outer ← ffiw.Build_Struct ['a' inner ⋄ 'b' inner]

⎕FX ffiw.Build_Function 'fn_c' ['s' outer⋄] ffiw.types.u32
fn_c ⎕NS⍬
fn_c (s: (a: (a: 1) ⋄ b: (b: 2)))

The autogenerated function will currently set all fields to 0 that aren't found with ⎕VGET, providing an equivalent to the aforementioned default initialization.

Returning Structs as Values

If a function returns a struct by value, the autogenerated function will return a namespace with those fields.

For example,

typedef struct Struct_A {
    uint32_t a;
    uint16_t b;
    uint32_t c;
    uint16_t d;
} Struct_A;

Struct_A fn_b(Struct_A a) {
    Struct_A out = {0};
    out.a = a.c;
    out.b = a.d;

    out.c = a.a;
    out.d = a.b;
    return out;
}
struct_a ← ffiw.Build_Struct [
    'a' ffiw.types.u32
    'b' ffiw.types.u16
    'c' ffiw.types.u32
    'd' ffiw.types.u16
]
⎕FX ffiw.Build_Function 'fn_b' ['s' struct_a⋄] struct_a

res ← fn_b (s: (a: 1 ⋄ b: 2 ⋄ c: 3 ⋄ d: 4))
⎕ ← 'fn_b struct return: ', ⍕res.a res.b res.c res.d

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •