Interface Definition¶
The Interface Definition Language (IDL) is used to define the interface between C++ and the host language.
Built-In Types¶
PyDjinni comes with a set of built-in data types. Consult the Types reference for a full list of all available types.
Optional types¶
Suffixing a type with a ?
marks it as optional. Optional values may be null.
Enums¶
Flags¶
Record¶
Records are pure data value objects, that combine multiple fields into a data class.
Extensions¶
To support extra fields and/or methods, a record can be "extended" in any language. To extend a record in a language, you can add a language target flag after the record tag. The generated type will have a Base suffix, and you should create a derived type without the suffix that extends the record type.
The derived type must be constructible in the same way as the Base type. Interfaces will always use the derived type.
Deriving¶
For record types, Haskell-style "deriving" declarations are supported to generate some common methods. This way for example equality and order comparators or a method for giving a string representation of the record can be added. Not all features may be available in all target languages. Consult the Deriving Reference for a full list of available declarations.
Interface¶
Interfaces are objects with defined methods to call (in C++, passed by shared_ptr
).
PyDjinni produces code allowing an interface implemented in C++ to be transparently used from Objective-C or Java
and vice versa.
# This interface will be implemented in C++
# and can be called from any language.
my_cpp_interface = interface +cpp {
method_returning_nothing(value: i32);
method_returning_some_type(key: string) -> data;
static get_version() -> i32;
}
# This interface will be implemented
# in Java or Objective-C and can be called from C++.
my_client_interface = interface -cpp {
log_string(str: string) -> bool;
}
Special Methods for C++ only¶
+cpp
interfaces (implementable only in C++) can have methods flagged with the special keywords const
and static
which have special effects in C++:
const
methods will be declared asconst
in C++, though this cannot be enforced on callers in other languages, which lack this feature.static
methods will become astatic
method of the C++ class, which can be called from other languages without an object. This is often useful for factory methods to act as a cross-language constructor.
Main Interface¶
Usually a PyDjinni library will have one or more entrypoints, in the form of an interface
with a static
constructor.
When using the library from Java, it is vital to ensure that the underlying native JNI library is loaded before
calling the constructor.
Usually this requires a System.loadLibrary("FooBar");
call ahead of time.
To automate the native library loading, any C++ interface can be marked as main
:
Given that the name of the native library is configured in the generator.java.native_lib
property, a static
initialization block is added to the interface, ensuring that the native library is loaded automatically.
Throwing exceptions¶
Generated C++ Methods are marked as noexcept
by default. By marking methods as throws
in the IDL,
automatic exception translation can be enabled:
Any exception raised in C++ is now translated to the target language and vice versa.
Async¶
The async
modifier can be used to specify that a method is asynchronous.
Asynchronous methods are implemented as C++ coroutines and are mapped to similar asynchronous execution models in each target language:
- Java:
CompletableFuture
- .NET:
Task
- Objective-C:
completion
handlers
When calling an asynchronous method in C++, the coroutine will automatically continue execution in a separate thread managed by the host language's default thread pool. This provides a convenient programming model for non-blocking execution of long-running tasks, such as network requests or file I/O operations across language boundaries.
Errors¶
Errors are specialized exception types that can optionally transport additional error data.
They are grouped inside an error domain type, similar to the concept of NSError
domains and codes in Objective-C.
Methods can then be marked to throw a specific error domain:
Every error can optionally be provided with an error message by default, and can be thrown from either C++ or the host language. When raised, the exception will be translated to its counterpart in the other language.
Functions¶
Functions can be passed to and returned from interface methods.
They are represented by std::function
in C++,
@FunctionalInterface
in Java,
blocks
in Objective-C and delegate
in C++/CLI (.NET).
Functions can either be defined like a type, or can be defined inline where they are needed as anonymous functions:
named_func = function (input: i32) -> bool;
foo = interface {
register_callback(callback: named_func);
register_anonymous_callback(callback: (input: i32) -> bool);
}
There is a short and a long form for defining functions. The long form allows target flags to be added in order to optimize the generated code:
The short form doesn't allow for code optimization, but in return is a lot more brief:
Namespaces¶
The interface file can be structured with namespaces:
The specified namespace is appended to the base namespace that is defined in the generator configuration.
Comments¶
Comments starting with #
are converted to documentation comments in the generated interfaces.
CommonMark flavored Markdown can be used to format the text.
Types inside inline code blocks with double backticks will be resolved and transformed to the generated type in
each target language: ``foo``
.
The following special commands are available:
Command | Description |
---|---|
@returns { description } |
documents the return value of a method |
@deprecated { description } |
marks a type, field or method as deprecated |
@param <name> { description } |
documents a method parameter |
@throws <type_ref> { description } |
documents an exception type that a method may throw |
Both JavaDoc or Doxygen style commands can be used: @deprecated
, \deprecated
.
ANTLR grammar¶
The grammar of the IDL is defined with ANTRL. The grammar below is the actual definition used by the parser:
grammar Idl;
idl : load* namespaceContent* EOF;
load : importDef | extern;
comment : COMMENT+;
importDef : IMPORT filepath;
extern : EXTERN filepath;
filepath : FILEPATH;
namespace : comment? NAMESPACE nsIdentifier LBRACE namespaceContent* RBRACE;
namespaceContent : typeDecl | namespace;
typeDecl : enum | flags | record | interface | namedFunction | errorDomain;
enum : comment? identifier ASSIGN ENUM LBRACE item* RBRACE;
item : comment? identifier SEMI;
flags : comment? identifier ASSIGN FLAGS LBRACE flag* RBRACE;
flag : comment? identifier modifier? SEMI;
modifier : ASSIGN ID;
record : comment? identifier ASSIGN RECORD targets LBRACE field* RBRACE deriving?;
field : comment? identifier COLON typeRef SEMI;
typeRef : dataType | function;
dataType : nsIdentifier (LT (dataType COMMA)* dataType GT)? OPTIONAL?;
function : (FUNCTION targets)? LPAREN ((parameter COMMA)* parameter)? RPAREN throwing? (ARROW typeRef)?;
throwing : THROWS ( (typeRef COMMA)* typeRef)?;
targets : TARGET*;
parameter : identifier COLON typeRef;
deriving : DERIVING LPAREN ((declaration COMMA)* declaration)? RPAREN;
declaration : ID;
interface : comment? identifier ASSIGN MAIN? INTERFACE targets LBRACE (method | prop)* RBRACE;
namedFunction : comment? identifier ASSIGN function SEMI;
method : comment? STATIC? CONST? ASYNC? identifier LPAREN ((parameter COMMA)* parameter)? RPAREN throwing? (ARROW typeRef)? SEMI;
errorDomain : comment? identifier ASSIGN ERROR LBRACE errorCode* RBRACE;
errorCode : comment? identifier (LPAREN ((parameter)* parameter)? RPAREN)? SEMI;
prop : comment? PROPERTY identifier COLON typeRef SEMI;
identifier : ID;
nsIdentifier : NS_ID | ID;
IMPORT : '@import';
EXTERN : '@extern';
NAMESPACE : 'namespace';
ENUM : 'enum';
FLAGS : 'flags';
STATIC : 'static';
CONST : 'const';
MAIN : 'main';
INTERFACE : 'interface';
RECORD : 'record';
DERIVING : 'deriving';
FUNCTION : 'function';
PROPERTY : 'property';
ASYNC : 'async';
ERROR : 'error';
THROWS : 'throws';
ARROW : '->';
OPTIONAL : '?';
ASSIGN : '=';
COLON : ':';
LPAREN : '(';
RPAREN : ')';
LBRACE : '{';
RBRACE : '}';
GT : '>';
LT : '<';
SEMI : ';';
COMMA : ',';
DOT : '.';
FILEPATH : '"' .*? '"';
TARGET : ('+' | '-') [a-z]+;
COMMENT : '#' ~[\r\n]*;
WS : [ \t\r\n]+ -> skip;
ID : Letter LetterOrDigit*;
NS_ID : ('.'? ID )+;
fragment LetterOrDigit : Letter | [0-9_];
fragment Letter : [a-zA-Z];