Skip to content

Modularization and Library Support

When generating the interface for your project and wish to make it available to other users in all of C++/Objective-C/Java/C# you can tell Djinni to generate a special YAML file as part of the code generation process. This file then contains all the information Djinni requires to include your types in a different project. Instructing Djinni to create these YAML files is controlled by the YAML generation parameters.

Caution

External types defined in YAML are not yet supported for Python.

YAML file structure

Such a YAML file looks as follows:

---
name: mylib_record1
typedef: 'record +c deriving(eq, ord)'
params: []
prefix: 'mylib'
cpp:
  typename: '::mylib::Record1'
  header: '"MyLib/Record1.hpp"'
  byValue: false
objc:
  typename: 'MLBRecord1'
  header: '"MLB/MLBRecord1.h"'
  boxed: 'MLBRecord1'
  pointer: true
  hash: '%s.hash'
objcpp:
  translator: '::mylib::djinni::objc::Record1'
  header: '"mylib/djinni/objc/Record1.hpp"'
java:
  typename: 'com.example.mylib.Record1'
  boxed: 'com.example.mylib.Record1'
  reference: true
  generic: true
  hash: '%s.hashCode()'
jni:
  translator: '::mylib::djinni::jni::Record1'
  header: '"Duration-jni.hpp"'
  typename: jobject
  typeSignature: 'Lcom/example/mylib/Record1;'
cs:
  translator: '::djinni::Record1_h'
  header: '"Record1-cs.hpp"'
  typename: 'Record1^'
  reference: false
---
name: mylib_interface1
typedef: 'interface +j +o +s'
    (...)
---
name: mylib_enum1
typedef: 'enum'
    (...)

Each document in the YAML file describes one extern type.

You can also check these files for some real working examples of what you can do with it:

Usage

To use a library type in your project simply include it in your IDL file and refer to it using its name identifier:

@extern "mylib.yaml"

client_interface = interface +c {
  foo(): mylib_record1;
}

Defining custom types

The YAML files can be created by hand as long as you follow the required format. This allows you to support types not generated by Djinni.

See duration.yaml and the accompanying translators in Duration-objc.hpp, Duration-jni.hpp and Duration-cs.hpp for an advanced example.

Handwritten translators implement the following concept:

// For C++ <-> Objective-C
struct Record1
{
    using CppType = ::mylib::Record1;
    using ObjcType = MLBRecord1*;

    static CppType toCpp(ObjcType o) { return /* your magic here */; }
    static ObjcType fromCpp(CppType c) { return /* your magic here */; }

    // Option 1: use this if no boxing is required
    using Boxed = Record1;
    // Option 2: or this if you do need dedicated boxing behavior
    struct Boxed
    {
        using ObjcType = MLBRecord1Special*;
        static CppType toCpp(ObjcType o) { return /* your magic here */; }
        static ObjcType fromCpp(CppType c) { return /* your magic here */; }
    }
};
// For C++ <-> JNI
#include "djinni_support.hpp"
struct Record1
{
    using CppType = ::mylib::Record1;
    using JniType = jobject;

    static CppType toCpp(JniType j) { return /* your magic here */; }
    // The return type *must* be LocalRef<T> if T is not a primitive!
    static ::djinni::LocalRef<jobject> JniType fromCpp(CppType c) { return /* your magic here */; }

    using Boxed = Record1;
};
// For C++ <-> C++/CLI
public ref class Record1 {
public:
    // Record1 public properties
internal:
    using CppType = ::mylib::Record1;
    using CsType = Record1^;
    static CppType ToCpp(CsType cs) { return /* your magic here */; }
    static CsType FromCpp(const CppType& cs) { return /* your magic here */; }
private:
    // Record1 properties' backing fields
}

For interface classes the CppType alias is expected to be a std::shared_ptr<T>.

Be sure to put the translators into representative and distinct namespaces.

If your type is generic the translator takes the same number of template parameters. At usage each is instantiated with the translators of the respective type argument.

template<class A, class B>
struct Record1
{
    using CppType = ::mylib::Record1<typename A::CppType, typename B::CppType>;
    using ObjcType = MLBRecord1*;

    static CppType toCpp(ObjcType o)
    {
        // Use A::toCpp() and B::toCpp() if necessary
        return /* your magic here */;
    }
    static ObjcType fromCpp(CppType c)
    {
        // Use A::fromCpp() and B::fromCpp() if necessary
        return /* your magic here */;
    }

    using Boxed = Record1;
};

Last update: May 2, 2021