Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement runtime binding generation #923

Draft
wants to merge 19 commits into
base: master
Choose a base branch
from

Conversation

kang-sw
Copy link

@kang-sw kang-sw commented Jan 31, 2024

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 Introduce language backend #853; then rebase

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants