Closed GavinRay97 closed 3 years ago
There could be some unhandled C++ language constructs and edge cases, and you might just hit one.
Github releases are without debug info, unfortuntely there is no practical way to publish debug builds due to it's size as it can reach 80 GB with debug data. The only way is to do debug build locally, but be aware building clang can take hours, and debug builds on Windows is particularly slow due to MS STL bounds checks and stuff.
I'll try this myself and see what's wrong since I already have debug build.
crashes on
CXXMethodDecl 0x24f68dd1338 <D:\prog\juce_audio_plugin_client_amalgamated.h:15239:9, line:15252:9> line:15239:14 referenced simplify 'void ()'
source
void simplify()
{
for (int i = ranges.size(); --i > 0;) <--- here
{
auto& r1 = ranges.getReference(i - 1);
auto& r2 = ranges.getReference(i);
if (r1.getEnd() == r2.getStart())
{
r1.setEnd(r2.getEnd());
ranges.remove(i);
}
}
}
Well... two possible issues, some weird logic with unary --i or lack of for loop increment expression.
Ah, thank you for looking into that for me. Much appreciated.
Seems like part of the issue is that I ought to have done a manual pass and stripped out any function bodies too, since I think the only way this will work is to build the JUCE .dll and link/load it in D.
That way only function signatures should be needed. (I think -- I've not much experience with native/systems programming so this is all new to me)
Also at 90,000 lines, I think I'd have to buy you quite a few beers if Ohmygentool could pull that off (with or without some manual finessing, ha) :sweat_smile:
This is a pipe-dream project for me but would open up a lot of doors (JUCE is so complex nobody's ported bindings outside of C++) so I figured I'd give it a shot.
By the way, have you come across techniques for improving chance of successful translation? What I've tried:
ifdef
etc blocks which don't apply for target platform
I haven't gotten unifdef
or coan
working quite right but they seem generally on track. Maybe there's something better.
A pipeline like this seems like Maybe your best chance:
amalgamate | unifdef $(DEFINES) $(UNDEFINES) | gentool
You don't really need to do anything platform(or rather compiler) specific, gentool is basically a clang and behaves like clang. In some cases you need to add custom defines to make it pretend as MSVC, all that unmatched ifdefs will not get into the produced bindings (just at this moment, definitely will be useful in the future if done right).
The real problem is actually STL usage, JUCE has stuff like this
class JUCE_API LocalisedStrings {
...
std::unique_ptr<LocalisedStrings> fallback;
}
The problem is, unique_ptr support is incomplete, it is kind of there but is not ready, nor finished https://dlang.org/phobos/core_stdcpp_memory.html#.unique_ptr
The other problem is ConsoleApplication::Command
struct ConsoleApplication
{
struct Command
{
...
std::function<void(const ArgumentList&)> command;
}
...
}
std::function is a template, pragma mangle won't help that much and 'function' is reserved keyword in D so usually we just append underscore like 'function_', which will mess up naming scheme and this stuff won't link. It will need pragma mangle applied per instantiation which makes it impractical to do so by hand.
Another issue with std::function is that it actually can be 8 (single pointer, static function), 16 (two pointers, class method IIRC) or "random" size in bytes (when takes lambda), no guarantee it will ever work, I can't even say if it will work at all, need further investigation.
@cppsize(64) public function!(void function(ref const(ArgumentList))) command;
With such projects I would rather make opposite bindings, for example I have little OpenCV app in D where I just API as an abstract class and then handed it to C++ to access D data and write back the result, no manual fiddling (almost) thanks to -HC* flags in both DMD/LDC that generates extern(C++) definitions to .h
module glue.interfaces;
extern(C++)
struct Rect
{
int x, y, w, h;
this(int x, int y, int w, int h) { this.x = x; this.y = y; this.w = w; this.h = h; }
}
extern(C++)
abstract class ObjectDetector
{
// Image data
void getImageData(ref void* data, ref int w, ref int h, ref int bpp);
// Add object to list
void addDetectedObject(Rect r, int label, float accuracy);
// Clear objects list
void clear();
}
extern(C) void run_mobilenet(ObjectDetector detector);
Ah I see what you mean
Better/easier to keep host application in C++ and write some D code then link it into the C++ app.
This seems easier, maybe I can have gentool translate headers for the main namespaces/interfaces in JUCE used:
https://github.com/juce-framework/JUCE/blob/master/examples/CMake/AudioPlugin/PluginEditor.cpp
Yes, definitely it is easier that way. Filter what you need first and probably make wrappers for things that simply doesn't translate.
For example one of the first functions , enable_if relies on C++ SFINAE which is basically primitive form of D static if/version, so you'll need to translate it by hand
extern(C++, "juce")
Type unalignedPointerCast(Type, enable_if!(is_pointer!(Type).value, int).type = 0)(void* ptr) {
return cast(Type)(ptr);
}
Or this case, while it will work as is in D you can make it much more efficient, also look a the Type, it is translated as it looks (parameterless template with empty body). However I'm unsure if it has same logic on type size in D. edit: it is not, doesn't even compiles
extern(C++, "juce","detail")
struct Type()
{
}
extern(C++, "juce","detail")
size_t getMaxAlignment(){
return 0;
}
extern(C++, "juce","detail")
size_t getMaxAlignment(Head, Tail)(Type!(Head) , Type<Tail>... tail){
return jmax((Head).alignof, getMaxAlignment(tail));
}
size_t maxAlignment = getMaxAlignment(Type!(max_align_t){}, Type!(void*){}, Type!(float){}, Type!(double){}, Type!(long double){}, Type!(short){}, Type!(int){}, Type!(cpp_long){}, Type!(long){}, Type!(bool){}, Type!(char){}, Type!(char16_t){}, Type!(char32_t){}, Type!(wchar_t){});
btw I just found more uncovered cases.
Oh wow 😱
extern(C++, "juce","detail")
size_t getMaxAlignment(Head, Tail)(Type!(Head) , Type<Tail>... tail){
return jmax((Head).alignof, getMaxAlignment(tail));
}
size_t maxAlignment = getMaxAlignment(Type!(max_align_t){}, Type!(void*){}, Type!(float){}, Type!(double){}, Type!(long double){}, Type!(short){}, Type!(int){}, Type!(cpp_long){}, Type!(long){}, Type!(bool){}, Type!(char){}, Type!(char16_t){}, Type!(char32_t){}, Type!(wchar_t){});
Yes this does not seem very practical/feasible to translate sadly Ah well, maybe someday!
I'm trying to convert the headers for the JUCE C++ SDK for making audio plugins to D. Tried to do this with just
gentool-config.json
config but too many errors (the build/dependency is hell).But I managed to use an amalgation tool to start from an entrypoint header and have it replace
#include
with file content recursively to generate a single-file.https://github.com/rindeal/Amalgamate
Running the following on the JUCE sources: https://github.com/juce-framework/JUCE
Produces the below (minus manual removal of
#pragma once
and a handful of other redefined pragmas) https://gist.github.com/GavinRay97/b8bdb882a391e63cb7e2f73b247d4a6e(Raw, it's huge): https://gist.githubusercontent.com/GavinRay97/b8bdb882a391e63cb7e2f73b247d4a6e/raw/87ddc1678079b7034b91229660a9ad160af0ed5a/juce_audio_plugin_client_amalgamated.h
Running it with this configuration:
Maybe it's just too big/complex to be translated?