Superbelko / ohmygentool

LLVM/Clang based bindings generator for D language
The Unlicense
39 stars 6 forks source link

[Suggestion]: Ask users to post repos of successful translations using OMG, keep list of successes on readme #16

Open GavinRay97 opened 3 years ago

GavinRay97 commented 3 years ago

I think this would be a really cool way to share translations of useful libs with others, and also to show examples of what Ohmygentool is capable of, and the quality + accuracy of code it can produce 😃

I would be willing to start, I would just need to create repos for:

Also I believe Imperatorn had some degree of success translation both header types & function body code for this repo, which includes PDCurses in-tree:

I think it would also probably be helpful to see other people's gentool-config.json and notes on manual changes (if any) to the source to get it to compile, or changes to generated code. This way people could pick up on helpful tricks and common approaches.

Where the repo readme is something like this maybe:

Z3 C API translated to D

Generated: (gist here in lieu of actual source) https://gist.github.com/GavinRay97/66f301b5a905de6b1739688553699253

How to generate

// gentool-config.json
{
  "$schema": "./gentool-config.schema.json",
  "version": 1,
  "input": {
    "std": "c++20",
    "system": [],
    "defines": ["_WIN32=1", "_WIN64=1", "Z3_API="],
    "cflags": ["-Wno-pragma-once-outside-header" "-Wno-return-type-c-linkage"],
    "includes": ["../", "./"],
    "paths": [
      "./z3_algebraic.h",
      "./z3_api.h",
      "./z3_ast_containers.h",
      "./z3_fixedpoint.h",
      "./z3_fpa.h",
      "./z3_optimization.h",
      "./z3_polynomial.h",
      "./z3_private.h",
      "./z3_rcf.h",
      "./z3_replayer.h",
      "./z3_spacer.h"
    ]
  },
  "output": {
    "path": "./z3.d",
    "target": "dlang",
    "attr-nogc": false,
    "skip-bodies": true
  }
}
/**
 * THIS FILE IS USED AS A PRELUDE HEADER FOR THE C API HEADERS
 * SO THAT "Ohmygentool" CAN SUCCESSFULLY PARSE THE DEFINITIONS
 */
#pragma once
#include <stdint.h>

// The original version of this breaks Ohmygentool:
//    #define DEFINE_TYPE(T) typedef struct _ ## T *T
// Gives: "addType() error: empty path for entry <T>"
#define DEFINE_TYPE(T) struct T;

#ifndef Z3_bool_opt
#define Z3_bool_opt Z3_bool
#endif

#ifndef Z3_API
#ifdef __GNUC__
#define Z3_API __attribute__((visibility("default")))
#else
#define Z3_API
#endif
#endif

typedef bool Z3_bool;
typedef const char *Z3_string;
typedef char const *Z3_char_ptr;
typedef Z3_string *Z3_string_ptr;

#define Z3_TRUE true
#define Z3_FALSE false

DEFINE_TYPE(Z3_symbol);
// ... omitted for brevity
Superbelko commented 3 years ago

Maybe it generates them, but does it links? ( ͡° ͜ʖ ͡°)

Yep, addType is macro expanded to typedef, that's second annoyance after templates.
Though it's not technically broken, it just expands out of context.

As for generated stuff, be careful with these, it is generated for your host platform, if you build with it on another OS it might introduce subtle bugs because of ABI difference, so for now always (re)generate on target platform.

There is also subtle detail about __gshared static globals when you link against libraries, it will introduce second symbol and result is unpredictable, so it also needs to be marked export depending on link target.

P.S. $schema field is for VS Code JSON intellisense, maybe it'll work if you point to schema file on github, it does works locally though

GavinRay97 commented 3 years ago

There is also subtle detail about __gshared static globals when you link against libraries, it will introduce second symbol and result is unpredictable, so it also needs to be marked export depending on link target.

Ahh I did not know this, thanks for telling me

P.S. $schema field is for VS Code JSON intellisense, maybe it'll work if you point to schema file on github, it does works locally though

I use VS Code but I also didn't know you could use an URL for the schema. I have been copying it to every project folder manually 😂 I will link to Github raw URL instead

Maybe it generates them, but does it links? ( ͡° ͜ʖ ͡°)

Ha, I should try to link the z3 one. It's a C API so I'd imagine it would -- that's maybe not the most impressive demo 😅

The REAPER one worked fantastic though, I have built stuff with it, it did a full working translation.

(Though for that one, it was actually not linked, but instead the entrypoint is called by a third-party binary, with a function used to get the fn-ptrs to the methods in the header)

// ./reaper/generated.d
module reaper.generated;
extern (C++) @cppclasssize(32) align(8) struct reaper_plugin_info_t
{

    @cppsize(4) public int caller_version;
    @cppsize(8) public HWND hwnd_main;
    @cppsize(8) public int function(const(char)*, void*) Register;
    @cppsize(8) public void* function(const(char)*) GetFunc;
}
// Struct just used for __traits reflection
// to load the function pointers into the generated definitions at runtime
__gshared struct Reaper
{
static:
    bool function(const(char)*, const(char)*, const(char)*, bool) AddCustomizableMenu;
    bool function() AddExtensionsMainMenu;
    MediaItem* function(MediaTrack*) AddMediaItemToTrack;
      // etc...
    void function(const(char)*) ShowConsoleMsg;
}

// app.d
import reaper = reaper.generated;

void load_reaper_api(reaper.reaper_plugin_info_t* rec)
{
    foreach (name; __traits(allMembers, reaper.Reaper))
    {
    // reaper.Reaper.ShowConsoleMsg = cast(typeof(reaper.Reaper.ShowConsoleMsg) rec.GetFunc("ShowConsoleMsg");
    mixin(`alias tmp = reaper.Reaper.`, name);
    mixin(q{
        tmp = cast(typeof(tmp)) rec.GetFunc(name);
        if (tmp is null) throw new Exception("Failed to load REAPER function: " ~ name);
    });
    }
}

extern (C) export int ReaperPluginEntry(HINSTANCE hInstance, reaper.reaper_plugin_info_t* rec)
{
    load_reaper_api(rec);
    reaper.Reaper.ShowConsoleMsg("Hello from Dlang!");
    return 1;
}

In fact, I saw this trick recently from your .NET host repo, and I have been saving it to clean that up:

// https://github.com/Superbelko/dotnethost-d/blob/7bee104c844b2753bfcd326371a6ac2dea8e438a/source/dotnet/host.d#L154-L162
mixin template InjectFunctionPointers(T)
{
    static foreach (fptr; __traits(allMembers, T))
    {
        static if (isFunctionPointer!(__traits(getMember, T, fptr)))
        {
            mixin(typeof(__traits(getMember, T, fptr)).stringof, " ", fptr, ";");
        }
    }
}

// Modify it to take a function for loading the function pointer, then:
InjectFunctionPointers!(reaper.Reaper)
LorenDB commented 2 years ago

+1 for this idea. I'd love to use this to generate bindings to Wt, but I am struggling to do so.

Superbelko commented 2 years ago

Open up a new issue with help request, I'll try to help sort it out. Though as I see Wt relies heavily on STL and Boost, this is serious problem for the current generator, without lots of manual tweaking of the produced code there is no way it will work.