dropbox / djinni Goto Github PK
View Code? Open in Web Editor NEWA tool for generating cross-language type declarations and interface bindings.
License: Apache License 2.0
A tool for generating cross-language type declarations and interface bindings.
License: Apache License 2.0
When defining a interface +j
with a record type, ADL seems to be selecting the wrong types.
record_thing = record {
data_member: string;
}
interface_thing = interface +j +o {
on_event(thing: record_thing);
}
Generated code with comments:
namespace djinni_generated_java_classes {
void InterfaceThing::JavaProxy::JavaProxy::on_event(const ::my_namespace::RecordThing & c_thing) {
JNIEnv * const jniEnv = djinni::jniGetThreadEnv();
djinni::JniLocalScope jscope(jniEnv, 10);
djinni::LocalRef<jobject> j_thing(jniEnv, RecordThing::toJava(jniEnv, c_thing));
// selects my_namespace::InterfaceThing NOT djinni_generated_java_classes::InterfaceThing
const InterfaceThing & data = djinni::JniClass<InterfaceThing>::get();
// data.method_onEvent is undefined
jniEnv->CallVoidMethod(getGlobalRef(), data.method_onEvent, j_thing.get());
djinni::jniExceptionCheck(jniEnv);
};
}
Running djinni on windows fails due to generating target/start.bat but trying to exec target/start from run_assemble_built
For running on cygwin, it should generate a normal "start" and exec it. Or, if it's running from a windows command prompt it should "start /WAIT start.bat". The cygwin approach seems less problematic...
If a C++ api ever returns nullptr, djinni wraps it in non-null objects in objc and java.
for example, if run below dijinni definition with cpp-namespace option,
platform_obj = interface +o +j {
}
platform_api = interface +o +j {
Obj(): platform_obj;
}
the generated obj code will miss cpp-namespace prefix.
I am not familiar with scala, but I tried to change this file ObjcGenerator.scala with line 330 and 358:
val ret = m.ret.fold("void")(toCppType(, spec.cppNamespace))
val ret = m.ret.fold("void")(toCppType(, spec.cppNamespace))
it seems work but I really dont know scala much, please help me to check if it's ok or not, thanks.
Hi,
at the CppCon talk you mentioned why there is only f64
, something regarding inconsistent sizes across iOS architectures (it's been a while). However that only applies to CGFloat
which is an alias to either float
or double
, depending on whether it runs on arm64 or not. The actual values of sizeof(float)
and sizeof(double)
always give 4
and 8
, respectively. On Java the aliases jfloat
and jdouble
already do the right thing.
So I'm not entirely sure I understand why there is only f64
.
Regards
Miro
Error message on Mac:
NDK is not configured. Make sure there is a local.properties file with an ndk.dir entry in the directory
Following the instructions in README.md I get these errors:
$ make android
make: *** No rule to make target `android'. Stop.
$ make ios
make: *** No rule to make target `ios'. Stop.
$ make example_android
...
cd example/android/ && ./gradlew app:assembleDebug
FAILURE: Build failed with an exception.
* Where:
Build file 'djinni/example/android/app/build.gradle' line: 38
* What went wrong:
A problem occurred evaluating project ':app'.
> NDK is not configured. Make sure there is a local.properties file with an ndk.dir entry in the directory djinni/example/android.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Suggestions:
Since local.properties
is excluded in gitignore, creating a local.properties.sample
would be a great help.
Also updating the README.md with the steps required to download and install SDK/NDK would allow prospective users to quickly compile & test.
C# support would allow for easy Windows Phone integration.
Hello, just started working with djinni, tried to run make
and it failed.
cd example/android/ && ./gradlew app:assembleDebug
FAILURE: Build failed with an exception.
* Where:
Build file '/Users/nt/dev/mac/lib/djinni/example/android/app/build.gradle' line: 17
* What went wrong:
A problem occurred evaluating project ':app'.
> Ambiguous method overloading for method java.io.File#<init>.
Cannot resolve which method to invoke for [null, class java.lang.String] due to overlapping prototypes between:
[class java.lang.String, class java.lang.String]
[class java.io.File, class java.lang.String]
cd example/android/ && ./gradlew app:assembleDebug
FAILURE: Build failed with an exception.
* Where:
Build file '/Users/nt/dev/mac/lib/djinni/example/android/app/build.gradle' line: 17
* What went wrong:
A problem occurred evaluating project ':app'.
> Ambiguous method overloading for method java.io.File#<init>.
Cannot resolve which method to invoke for [null, class java.lang.String] due to overlapping prototypes between:
[class java.lang.String, class java.lang.String]
[class java.io.File, class java.lang.String]
Here is output of make https://gist.github.com/nt9/6115ffb92885cf4b8cd9
Would be glad if someone could give an advice :)
Related to #6 - there are also places where we'd like the Java versions of records to be Parcelable
.
I called Djinni with option --hpp-ext h
but the extensions of Djinni support header files are also changed too, for example there is HString.h
in a generated file instead of HString.hpp
.
The directory https://github.com/dropbox/djinni/tree/master/support-lib/jni does not have the file HI16.hpp. It does have files for the other basic types, e.g. HI32.hpp, HI64.hpp, etc.
Djinni should be able to autogenerate json parsing and serialization code for records/enums.
Right now interfaces can be {Java,ObjC}->C++ or C++->{Java,ObjC}, but the same interface can't be both, and the role is fixed at codegen time. We should be more flexible.
ObjC depends on autorelease to clean up memory in a timely fashion - without an autoreleasepool, ObjC temporaries created (esp. on background threads) can stick around for a very long time.
Djinni really only needs to know how to convert record types between objc, java and c++. What if we extended the include format to take plugins like this?
@include "date.json"
djinni_type: "custom_date"
objc_type: "NSDate"
java_type: "java.util.Date"
cpp_include_path: "<chrono>"
cpp_type: "std::chrono::system_clock::time_point"
jni_type: "jobject"
// custom_date.hpp
namespace djinni {
jobject custom_date_to_java(time_point);
NSDate * custom_date_to_objc(time_point);
time_point custom_date_to_cpp(jobject);
time_point custom_date_to_cpp(NSDate *);
}
Hey folks,
over the past month or two Djinni was used quite extensively here and we ran into a few problems, resulting in a number of refactorings, both internal (not changing the output) and external (significantly changing the output). Besides, we need something similar to #52, which at the time posed quite a challenge considering how Djinni was structured. So in preparation to make #52 (and others like #54, #46, #45) much easier to realize something had to happen.
Before doing a pull request I thought it'd be better to share these changes and get your input. So here we go:
ObjcGenerator
into ObjcGenerator
and ObjcppGenerator
. This makes it easier to adjust only one of them and is also how the Java/JNI generators are separated. The philosophy here is that using only ObjcGenerator
is sufficient to create all the Obj-C files necessary in the same fashion as JavaGenerator
only creates Java classes, but no JNI content.xxxGenerators
are responsible for the overall file structure, whereas xxxMarshals
do the actual language interop. So now JNIGenerator
can make use of CppMarshal
to create consistent type signatures etc. (This is a crucial part to make #52 remotely possible)@protocol
types for both +c
and +o
and provided a xxCppProxy
in a +Private.h
include file. This was a real thorn to every Obj-C developer exposed to Djinni generated types as it didn't really follow any existing conventions on that platform. Therefore came an implementation of #25. To recap: +c
types now generate an @interface
acting as proxy to C++ and +o
types generate a @protocol
to be implemented in Objc-C. Therefore +c
types are represented by T*
variables and +o
by id<T>
. There was similar outcry in the Java camp about +j
not creating a Java interface
instead of class
but changing the JNI side is a bit more involved.--objcpp-x
command line arguments:
--objcpp-out
: The output folder for private Objective-C++ files (Generator disabled if unspecified).--objcpp-include-prefix
: The prefix for #import
of Objective-C++ header files from Objective-C++ files.--objcpp-include-cpp-prefix
: The prefix for #include
of the main C++ header files from Objective-C++ files.--objcpp-include-objc-prefix
: The prefix for #import
of the Objective-C header files from Objective-C++ files (default: the same as --objcpp-include-prefix
)--objc-ext
=> --objcpp-ext
: The filename extension for Objective-C++ files (default: "mm"
)ObjcGenerator
is setup to create self-containing Obj-C records it creates its own .m
files containing the comparison functions, initializers, etc. separate from Obj-C++ translators. But because Obj-C only allows compile-time constants at file scope the file must have extension .mm
to support constants of record types. Same applies to interfaces.fromCpp()
functions now all return LocalRef<jobject>
instead of jobject
and local reference cleanup is handled by it form there on.::djinni
namespace for support library stuffThere may be other small fixes but those are the really important ones. It also includes fixes for issues #69, #26, #21, #10 and implicitly includes pull requests #67, #55, #42, #41, #39, #38 .
Do these changes break existing code? Hell yes, they do.
But at least the example and test-suite all pass.
I get that this kind of bulk change is a Bad Thing™, but never was there ever a point in the past 2 months where I could have said "yes, this is a small self-contained change that can be merged back now". There was (and still is) quite the amount of redundancy and intertwining. Also the reason why I'm doing this post first.
Anyway, the branch is here https://github.com/mknejp/djinni/tree/feature/codegen-refactor so check it out if interested and see how much code it breaks. You will definitely have to add the --objcpp-out
option otherwise that part is going to be missing entirely. If it's fine I'll do the pull request and hop on to #52.
When retrieving binary
data from Java environment, fromJava
method in HBinary.hpp calls jniExceptionCheck
which calls further methods.
The exception check call is made between the time GetPrimitiveArrayCritical
and ReleasePrimitiveArrayCritical
is called, causing undefined behaviour forbidden by the JVM standard.
It also causes a hard crash on Android ART / CheckJNI with an exception:
JNI DETECTED ERROR IN APPLICATION: thread Thread[13,tid=18055,Runnable,Thread*=0xaf075000,peer=<redacted>] using JNI after critical get
Suggest removing the check call in that method since it's usually done by the callee anyway.
The current way the "data" type works is only marginally useful for data streaming between languages due to all the copying involved.
I was thinking about introducing a "buffer" data type that represents memory shared between both sides of the fence without allocating and copying stuff around in every call. Both Objective-C and Java have facilities to access "unmanaged" regions of memory.
java.nio.ByteBuffer
which can be created in JNI with NewDirectByteBuffer()
and does not copy the content.NSData dataWithBytesNoCopy:length:freeWhenDone:
same as above (**)java.nio.ByteBuffer
and use GetDirectBufferAddress()
and GetDirectBufferCapacity()
to transform into something like std::experimental::array_view<uint8_t>
.NSMutableData
and use .length
and .mutableBytes
to construct the array view.The whole point of this exercise is to avoid copying the buffer content and is intended for long-lived buffers that are shared and written to/read from on both sides to exchange bulk data. There of course must be some sort of agreement in the interface protocol about who creates the data and make sure it is not modified in a way that invalidates the memory region.
On a related note, maybe the "data" datatype should also switch to something like std::experimental::array_view<const uint8_t>
to avoid the copy at least in one direction where possible.
** The only drawback here is that NSData
is read-only. If mutable access is necessary the buffer has to be created on the Objective-C side with NSMutableData
.
Hello, maybe it is not the best place to ask these kind of questions and this particular question is not the best too, but could you describe basic workflow for someone who is not so experienced in build systems and xplatform build process, maybe write something like short "how-to"? Thank you :)
The failure in cygwin is due to incorrect paths. Java on windows requires windows-formatted paths, even when run from cygwin.
cygpath can be used to convert between the various formats.
If you create a record without any non-const members (say, one to describe an app or library version...), the generated Objective-C constructors for it will produce unused-parameter warnings, since their arguments are never referenced. Perhaps we can just emit a "#pragma unused (varName)"?
For code bases that have ARC disabled, djinni doesn't send the dealloc message to the superclass.
That commit misdirects the compiler to look for names like sort_order
, ItemList
etc in global namespace instead of textsort
.
If you try to run djinni in a path with a space, it will fail:
var/lib/jenkins/jobs/foo bar/workspace/vendor/djinni/src/build: line 15: cd: /var/lib/jenkins/jobs/foo: No such file or directory
dirname: missing operand
Try dirname --help' for more information. dirname: missing operand Try
dirname --help' for more information.
The fix is in src/build,:
diff --git a/src/build b/src/build
index b3ccdac..c2f7d75 100755
--- a/src/build
+++ b/src/build
@@ -12,7 +12,7 @@ while [ -h "$loc" ]; do
loc="dirname "$loc"
/$link" # Relative link
fi
done
-base_dir=$(cd dirname "$loc"
&& pwd)
+base_dir=$(cd "$(dirname "$loc")" && pwd)
need_to_build=0
dependencies=("build.sbt" "project/plugins.sbt" "source")
I want to add support for a new language but the generators are tangled up.
For example there is code referencing each language generator in the generic generator class.
I don't think this scalable are you going to make it easier to add new languages.
I am trying to compile Djinni, but am running into some Java security exceptions. If anybody has any suggestions on how to switch off these security settings, that would be great.
dhruba@dev223 ~/djinni]$ java -version
java version "1.7.0_06"
Java(TM) SE Runtime Environment (build 1.7.0_06-b24)
Java HotSpot(TM) 64-Bit Server VM (build 23.2-b09, mixed mode)
[dhruba@dev223 ~/djinni]$ uname -a
Linux 3.2.55-106_fbk22_00877_g6902630 #106 SMP Wed Apr 2 16:19:59 PDT 2014 x86_64 x86_64 x86_64 GNU/Linux
[dhruba@dev223 ~/djinni]$ src/run
Building Djinni...
java -Xms512M -Xmx1024M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256M -Djava.security.manager -Djava.security.policy=/home/dhruba/local/djinni/src/support/sbt.security.policy -jar /home/dhruba/local/djinni/src/support/sbt-launch.jar -Dsbt.override.build.repos=true -Dsbt.repository.config=/home/dhruba/local/djinni/src/support/sbt.resolvers.properties compile start-script
[info] Loading project definition from /data/users/dhruba/djinni/src/project
[info] Updating {file:/data/users/dhruba/djinni/src/project/}src-build...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn] :: UNRESOLVED DEPENDENCIES ::
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn] :: com.typesafe.sbt#sbt-start-script;0.10.0: several problems occurred while resolving dependency: com.typesafe.sbt#sbt-start-script;0.10.0 {compile=[default(compile)]}:
[warn] java.security.AccessControlException: access denied ("java.net.SocketPermission" "xx:8080" "connect,resolve")
[warn] java.security.AccessControlException: access denied ("java.net.SocketPermission" "xx:8080" "connect,resolve")
[warn] java.security.AccessControlException: access denied ("java.net.SocketPermission" "xx:8080" "connect,resolve")
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn]
[warn] Note: Some unresolved dependencies have extra attributes. Check that these dependencies exist with the requested attributes.
[warn] com.typesafe.sbt:sbt-start-script:0.10.0 (sbtVersion=0.13, scalaVersion=2.10)
[warn]
sbt.ResolveException: unresolved dependency: com.typesafe.sbt#sbt-start-script;0.10.0: several problems occurred while resolving dependency: com.typesafe.sbt#sbt-start-script;0.10.0 {compile=[default(compile)]}:
java.security.AccessControlException: access denied ("java.net.SocketPermission" "xx.xx.xx.xx:8080" "connect,resolve")
java.security.AccessControlException: access denied ("java.net.SocketPermission" "xx.xx.xx.xx:8080" "connect,resolve")
java.security.AccessControlException: access denied ("java.net.SocketPermission" "xx.xx.xx.xx:8080" "connect,resolve")
at sbt.IvyActions$.sbt$IvyActions$$resolve(IvyActions.scala:217)
at sbt.IvyActions$$anonfun$update$1.apply(IvyActions.scala:126)
at sbt.IvyActions$$anonfun$update$1.apply(IvyActions.scala:125)
at sbt.IvySbt$Module$$anonfun$withModule$1.apply(Ivy.scala:115)
at sbt.IvySbt$Module$$anonfun$withModule$1.apply(Ivy.scala:115)
at sbt.IvySbt$$anonfun$withIvy$1.apply(Ivy.scala:103)
at sbt.IvySbt.sbt$IvySbt$$action$1(Ivy.scala:48)
at sbt.IvySbt$$anon$3.call(Ivy.scala:57)
at xsbt.boot.Locks$GlobalLock.withChannel$1(Locks.scala:98)
at xsbt.boot.Locks$GlobalLock.xsbt$boot$Locks$GlobalLock$$withChannelRetries$1(Locks.scala:81)
at xsbt.boot.Locks$GlobalLock$$anonfun$withFileLock$1.apply(Locks.scala:102)
at xsbt.boot.Using$.withResource(Using.scala:11)
at xsbt.boot.Using$.apply(Using.scala:10)
at xsbt.boot.Locks$GlobalLock.ignoringDeadlockAvoided(Locks.scala:62)
at xsbt.boot.Locks$GlobalLock.withLock(Locks.scala:52)
at xsbt.boot.Locks$.apply0(Locks.scala:31)
at xsbt.boot.Locks$.apply(Locks.scala:28)
at sbt.IvySbt.withDefaultLogger(Ivy.scala:57)
at sbt.IvySbt.withIvy(Ivy.scala:98)
at sbt.IvySbt.withIvy(Ivy.scala:94)
at sbt.IvySbt$Module.withModule(Ivy.scala:115)
at sbt.IvyActions$.update(IvyActions.scala:125)
at sbt.Classpaths$$anonfun$sbt$Classpaths$$work$1$1.apply(Defaults.scala:1223)
at sbt.Classpaths$$anonfun$sbt$Classpaths$$work$1$1.apply(Defaults.scala:1221)
at sbt.Classpaths$$anonfun$doWork$1$1$$anonfun$74.apply(Defaults.scala:1244)
at sbt.Classpaths$$anonfun$doWork$1$1$$anonfun$74.apply(Defaults.scala:1242)
at sbt.Tracked$$anonfun$lastOutput$1.apply(Tracked.scala:35)
at sbt.Classpaths$$anonfun$doWork$1$1.apply(Defaults.scala:1246)
at sbt.Classpaths$$anonfun$doWork$1$1.apply(Defaults.scala:1241)
at sbt.Tracked$$anonfun$inputChanged$1.apply(Tracked.scala:45)
at sbt.Classpaths$.cachedUpdate(Defaults.scala:1249)
at sbt.Classpaths$$anonfun$updateTask$1.apply(Defaults.scala:1214)
at sbt.Classpaths$$anonfun$updateTask$1.apply(Defaults.scala:1192)
at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:42)
at sbt.std.Transform$$anon$4.work(System.scala:64)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:237)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:237)
at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:18)
at sbt.Execute.work(Execute.scala:244)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:237)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:237)
at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:160)
at sbt.CompletionService$$anon$2.call(CompletionService.scala:30)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
error sbt.ResolveException: unresolved dependency: com.typesafe.sbt#sbt-start-script;0.10.0: several problems occurred while resolving dependency: com.typesafe.sbt#sbt-start-script;0.10.0 {compile=[default(compile)]}:
[error] java.security.AccessControlException: access denied ("java.net.SocketPermission" "xx.xx.xx.xx:8080" "connect,resolve")
[error] java.security.AccessControlException: access denied ("java.net.SocketPermission" "xx.xx.xx.xx:8080" "connect,resolve")
[error] java.security.AccessControlException: access denied ("java.net.SocketPermission" "xx.xx.xx.xx:8080" "connect,resolve")
Not much more info needed, but this is pretty confusing behavior if you're just getting started. Having two .hpp with exactly the same name seems brittle too / is pretty mindbending if you're trying to read the generated source for understanding.
I get the intention behind specifying an optional<T>
in the interface definition, stating that the passed in object reference may be nil
/null
in Obj-C or Java and this intention is clearly shown in the C++ side using optional<shared_ptr<T>>
as argument or return type.
My problem with this is twofold:
shared_ptr
already has a well defined empty state. Wrapping the whole thing inside optional
seems redundant.nil
or null
is OK or not, contrary to the C++ signature. The difference only manifests itself in the form of a runtime error if used incorrectly. Therefore whether a function accepts empty object references must be documented somewhere as one cannot infer it form the argument/return types.My conclusion is getting rid of optional<shared_ptr<T>>
and instead transform nil
/null
to/from empty shared_ptr<T>
for interface types, regardless of whether they are defined as T
or optional<T>
in the Djinni file. However for plain T
the generated code could handle nil
/null
/nullptr
as errors and throw an appropriate exceptions to the offending code with an explanatory error message along the lines of "argument xxx may not be null", "return value must not be null" and so on.
Does that sound like a sensible thing to do?
if you have the CIDL file
MyClassListener = interface +c {
onMyClassInitialized(instance: MyClass);
}
MyClass = interface +c {
static createMyClass(listener: MyClassListener);
}
then it will fail to compile because of a circular dependency. it would be rad to detect these and break them with forward declarations.
Hi,
the current code generation is broken for cases where a +c interface returns +j or +o interfaces.
Given:
extern = interface +j +o {...}
intern = interface +c { foo(): extern; }
On the JNI side this tries to call Extern::toJava()
, which does not exist, and for Obj-C it tries to static_pointer_cast
from Extern
to ExternObjcProxy
, which is mostly likely not the case and causes undefined behavior.
I think the easiest fix would be to disallow returning +j +o interfaces from +c. Supporting this properly would be a bit more involved I think.
Hi, I've tried to use djinni for calling C++ from Java on both Linux and MaxOSX but I get a java.lang.UnsatisfiedLinkError
exception at runtime. Is it supposed to be possible to use the C++/Java interop functionality of djinni on Linux and MaxOSX or is the implementation specific to the Android build/runtime system?
I'm very interested in experimenting with djinni for bridging C++ libraries with Java on Windows. However, I'm struggling to get started since there does not seem to be a getting started guide or list of build tool dependencies documented anywhere. Also, the documentation and makefiles already present seem very geared towards "mobile" Android/iOS platforms, with little "desktop" information to be found.
Could it be possible to extend the existing documentation with a little bit of "getting started" information for different platforms such as e.g. Windows/Linux/Android/OS-x/iOS?
Thanks in advance,
Fredrik
When a record is specified to be extended in ObjC (using the +o
flag), the deriving header location is always ../DerivingHeaderName.h
(see here). Now that header is obviously a piece of handwritten code. I could not make this work while keeping the generated and handwritten source files in separate directories.
Wouldn't it be better to add an option for specifying were handwritten source files are located? Or is there some other way around this?
It would be nice to have an example of a record extension in the included example.
list_ish = interface +c {
map(iterator_fn: function<i64>): list_ish;
}
This would generate a block based api for objc, and a Callable
(or similar one for java).
This should be flexible enough to encapsulate both synchronous and async functions.
When you try to use an enum as the key in a map, the compiler complains that there is no hash function for the enum
Idiomatic c++ wouldn't pass any primitive integer types by const&. I'm happy to write this up if it aligns with the goals of the project. I propose passing the following types by value.
T
if T is integraloptional<T>
if T is integralshared_ptr<T>
Failing with following error when trying to build using: make example_android
FAILURE: Build failed with an exception.
Where:
Build file '/home/prashant/Work/Djinni/example/android/app/build.gradle' line: 17
What went wrong:
A problem occurred evaluating project ':app'.
Ambiguous method overloading for method java.io.File#.
Cannot resolve which method to invoke for [null, class java.lang.String] due to overlapping prototypes between:
[class java.lang.String, class java.lang.String]
[class java.io.File, class java.lang.String]
Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
Please suggest
Something like --only-print-filenames
? That'd make it way easier to use Djinni in GYP actions.
A lot of the time you can just specify one of the directories or nothing at all under outputs
in a GYP action and still have the right thing happen, but if you wan to e.g. add your autogenned Objective-C headers to mac_framework_headers
for a framework target, you wind up having to do two passes if you're using something like glob.py: one to generate the files, and one for the build target to pick up the list of files to copy. (This issue can also be sidestepped if you check the autogenerated files into version control, but it's kind of lame for that to be the only way to make it work.)
Objective C always get an NSObject which is not null, but with a _cppRef = nullptr.
Accessing this element will crash application.
There's no way to guard against Cpp proxy returning nullptr.
When using djinni, it fails on trailing comments. Using comments not for documentation, but for commenting out methods / fields doesn't really work.
Works (because it still looks like a documentation comment)
blah = interface +c {
# method_a();
method_b();
}
Doesn't work but I would guess every single developer would expect it to
blah = interface +c {
method_a();
# method_b();
}
Hey guys, I think #4 is resurfacing.
If I have
ClientListener = interface +j +o {
onClientInitialized(client: Client);
}
Client = interface +c {
static createClient(baseUrl: string, listener: ClientListener);
}
Then I get an ObjectiveC ClientListener.h file referencing Client.h and a Client.h file referencing ClientListener.h.
This was tested on ed6ef3c
Thanks!
I'm trying to implement a delegate pattern:
device_discovery_delegate = interface +j +o {
device_added(device: device);
device_removed(device: device);
}
device = interface +c {
static add_delegate(delegate: device_discovery_delegate);
}
When doing this, the generated header Device.h imports DeviceDiscoveryDelegate.h which then references the protocol which is still not defined. I fixed this with this patch but I don't know if this is a good solution:
diff --git a/src/source/ObjcGenerator.scala b/src/source/ObjcGenerator.scala
index cd67a03..8d26b53 100644
--- a/src/source/ObjcGenerator.scala
+++ b/src/source/ObjcGenerator.scala
@@ -46,7 +46,7 @@ class ObjcGenerator(spec: Spec) extends Generator(spec) {
header.add("#import " + q(headerName(d.name)))
case DInterface =>
header.add("#import <Foundation/Foundation.h>")
header.add("#import " + q(headerName(d.name)))
header.add("@protocol " + idObjc.ty(d.name) + ";")
val ext = d.body.asInstanceOf[Interface].ext
if (ext.cpp) body.add("#import " + q(privateHeaderName(d.name + "_cpp_proxy")))
if (ext.objc) body.add("#import " + q(privateHeaderName(d.name + "_objc_proxy")))
MyEnum = enum {
blah;
bloop;
}
MyRecord = record {
myEnum: MyEnum;
}
Opening this in Objective-C throws the error "must use 'enum' to refer to type MyEnum in this scope" in the MyEnumTranslator.mm file. It also upper-cases the variable name myEnum so it becomes MyEnum, making the variable name and enum name conflict.
Right now, std::exception -> RuntimeException / NSException and that's it.
We want two things:
I would love an automatic way to generate async results for various platforms. This allows the idiomatic async pattern to be applied on each platform.
my_slow_class = interface + c {
do_long_operation(id: i64): async string;
}
This might generate the following:
C++
class MySlowClass {
//...
public:
void do_long_operation(int64_t id, const function<void(string)>& on_complete);
//...
};
Objc
@protocol DjinniMySlowClass
- (void)doLongOperation:(int64_t)id withCompletionBlock:(void (^)(NSString *))block;
@end
And maybe something with AsyncTask<Params, Progress, Result>
on android.
Djinni snippet:
network = interface +o +j {
download(params: map<network_params, string>): http_response;
}
This line fails to find std::vector:
@autoreleasepool {
std::vector<NSNumber *> cpp_paramsTempKeyVector;
Adding
This broke recently, it used to work.
Common workflow with djinni (I'm focused on Java only now): "Client code" (Java) --> C++ library shared code --> Platform specific interface if necessary (again Java, TextboxListenerImpl in the example).
If we call platform specific code from C++ and it throws an exception, then it is caught by jniExceptionCheck and passed back to C++ as jni_exception_pending(), but no matter what C++ code does (catches it or not), exception still exists in JVM and client code is forced to catch it (even if C++ code has already recovered).
Any reason for this? Can JNI remove exception from JVM with env->ExceptionClear() before throwing jni_exception_pending()? Then we will have to catch exception only once (in C++ code). "C++ code" does call platform specific code and it should know better how to recover in case of exception. If it is necessary we still can re-throw it to client code, but in some cases client code should not care about this.
Any thoughts?
Thank you.
Hi,
I am currently in the process of trying out Djinni for our projects here. One thing that I am curious about is why the Objective-C side is using @protocol for the base type, but additionally also relies on a public @interface.
Specifically, if I have MyClass I get a @ProtoColl names MyClass and an @interface called MyClassCppProxy. Now as long as one only uses instance methods with id it's nice and all. But this totally makes object construction very inconvenient using class methods.
Example:
@protocl MyClass
+ (id<MyClass>) create;
@end
At first glance one might think you can call [MyClass create] but while this is great with static Java/C++ member functions that's not how Objective-C protocols work. One has to use [MyClassCppProxy create] instead. This is very inconvenient. First, I have to tell users about classes that I would consider a private implementation detail, and second, it means I have to tell our (overly religious) Objective-C developers to use something with "cpp" in its name...
Besides the inconvenience, there might also be a correctness issue. Right now all generated +c interfaces on the Objective-C side take id as parameter and then internally cast it to SomethingCppProxy* to get to the C++ object. But the argument may not necessarily be an instance of SomethingCppProxy. While this will most likely just crash the program I'd rather have it prevented at compile time if possible.
Both these issues would be solved if Djinni only created @interface types for +c interfaces. But I guess there is some rationale behind the current design and I would be curious to know about it.
Regards,
Miro
Hi, In order to compile on Linux with gcc 4.9 I had to add #include <cstring>
to djinni_support.cpp
. Otherwise it couldn't find thea declaration for strrchr
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.