cbindgen icon indicating copy to clipboard operation
cbindgen copied to clipboard

Implement runtime binding generation

Open kang-sw opened this issue 2 years ago • 0 comments

Since this PR written from the changes not yet applied to master, file changes are a bit too verbose including non-PR changes. Actual change is only resides this file

As subsequent implementation of PR #853, implements C language backend for generating dynamic language bindings on runtime.

Two new structs are defined and exposed to public:

  • CDynamicBindingBackend
  • CDynamicBindingConfig

And user can use this to generate runtime-loadable C binding instead of static binding using following scheme.

      crate::Builder::new()
            .with_config(config)
            .with_crate(&CRATE_DIR)
            .generate()
            .unwrap()
            .write_with_backend(
                std::fs::File::options()
                    .write(true)
                    .create(true)
                    .open(OUT_FILE_NAME)
                    .unwrap(),
                &mut super::CDynamicBindingBackend::new("USER_API_STRUCT", Default::default()),
            );

This will generate global symbols and functions as loadable fields within the struct named as user provided USER_API_STRUCT string.

The generated loader methods will accept the Api definition struct and user-provided platform-agnostic API loader interface structure. Basically the generated loader methods is invisible to user. Once the user writes #define INCLUDE_CBINDGEN_LOADER_{USER_API_STRUCT} prior to the inclusion of generated header, then the loader methods is visible to the user as inline function.

Outputs with USER_API_STRUCT as "MyApiStruct"

C Backend

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct Bar Bar;

typedef struct {

} Foo;

extern const int32_t NUMBER;

extern Foo FOO;

extern const Bar BAR;

void root(void);

CDynamic Backend

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct Bar Bar;

typedef struct Foo {

} Foo;

struct MyApiStruct {
  const int32_t *NUMBER;
  struct Foo *FOO;
  const struct Bar *BAR;
  void (*root)(void);
};

#ifdef INCLUDE_CBINDGEN_LOADER_MyApiStruct
#  ifndef CBINDGEN_LOADER_LOOKUP_INTERFACE_DEFINED
#  define CBINDGEN_LOADER_LOOKUP_INTERFACE_DEFINED
struct CBindgenSymbolLookupIface {
    void* module;
    void* (*find_symbol)(void* module, const char* symbol_name);
    void* (*opt_find_function)(void* module, const char* function_name);
};
#  endif

#  ifndef CBINDGEN_LOADER_MyApiStruct_DEFINED
#  define CBINDGEN_LOADER_MyApiStruct_DEFINED

inline int MyApiStruct_load (
    struct MyApiStruct* api,
    struct CBindgenSymbolLookupIface* module
) {
    int notfound = 0;
    void* mod = module->module;
    void* (*fsym)(void*, const char*) = module->find_symbol;
    void* (*ffunc)(void*, const char*) = module->opt_find_function;
    
    if (!ffunc) {
        ffunc = module->find_symbol;
    }
    
    {
        api->NUMBER = (const int32_t*)fsym(mod, "NUMBER");;
        api->FOO = (struct Foo*)fsym(mod, "FOO");;
        api->BAR = (const struct Bar*)fsym(mod, "BAR");;

        api->root = (void(*)(void))fsym(mod, "root");;

        notfound += (int)!api->root;
        notfound += (int)!api->NUMBER;
        notfound += (int)!api->FOO;
        notfound += (int)!api->BAR;
    }
    
    return notfound;
}

#  endif
#endif

TODOs

  • [ ] Should configuration for CDynamic be merged into crate-wise Config?
  • [ ] Should we generate platform-specific module interface backends for user?
  • [ ] How should I write integration test code for this?
  • [ ] This is not tested yet enough; I'll apply this to my personal project first. Then elevate to this as merge request later.
  • [ ] Wait merging #853; then rebase

kang-sw avatar Jan 31 '24 03:01 kang-sw