Skip to content
This repository has been archived by the owner on Mar 26, 2020. It is now read-only.

Djinni Interface Inheritance #270

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
28 changes: 28 additions & 0 deletions example/generated-src/cpp/sort_items.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma once

#include <memory>
#include <string>

namespace textsort {

Expand All @@ -15,6 +16,33 @@ class SortItems {
public:
virtual ~SortItems() {}

/**
* Defines the name of the JNI C++ proxy class. Used to convert a
* C++ implemented object to a Java object when the type of the object being
* converted is unknown to the JniInterface (see djinni_support.hpp).
*
* The proxy class name is only used for converting Djinni interfaces that
* are implemented in C++. Java implemented interfaces are converted differently.
* However, the C++ class of an interface implemented in Java must still have a
* jniProxyClassName method in order for Djinni's JniInterface::fromCpp method to compile.
*
* @return The name of the class's associated JNI proxy class.
*
* @see JniInterface in djinni_support.hpp
*/
virtual const std::string jniProxyClassName() { return "com/dropbox/textsort/SortItems$CppProxy"; }
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The jniProxyClassName and objcProxyClassName are now generated for each interface to workaround type slicing when creating the Java and the Objective-C proxies. I would love to come up with a better approach to this, but I think it will require a large change to Djinni's proxy caching. This was a "good enough" solution for our needs.


/**
* Defines the name of the Objective-C type for the class. Used to convert a
* C++ object to an Objective-C object when the type of the object being
* converted is unknown to the C++ wrapper cache (see DJICppWrapperCache+Private.hpp).
*
* @return The name of the Objective C type associated with the class.
*
* @see get_cpp_proxy function in DJICppWrapperCache+Private.hpp
*/
virtual const std::string objcProxyClassName() { return "TXSSortItems"; }

/** For the iOS / Android demo */
virtual void sort(sort_order order, const ItemList & items) = 0;

Expand Down
29 changes: 29 additions & 0 deletions example/generated-src/cpp/textbox_listener.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#pragma once

#include <string>

namespace textsort {

struct ItemList;
Expand All @@ -11,6 +13,33 @@ class TextboxListener {
public:
virtual ~TextboxListener() {}

/**
* Defines the name of the JNI C++ proxy class. Used to convert a
* C++ implemented object to a Java object when the type of the object being
* converted is unknown to the JniInterface (see djinni_support.hpp).
*
* The proxy class name is only used for converting Djinni interfaces that
* are implemented in C++. Java implemented interfaces are converted differently.
* However, the C++ class of an interface implemented in Java must still have a
* jniProxyClassName method in order for Djinni's JniInterface::fromCpp method to compile.
*
* @return The name of the class's associated JNI proxy class.
*
* @see JniInterface in djinni_support.hpp
*/
virtual const std::string jniProxyClassName() { return nullptr; }

/**
* Defines the name of the Objective-C type for the class. Used to convert a
* C++ object to an Objective-C object when the type of the object being
* converted is unknown to the C++ wrapper cache (see DJICppWrapperCache+Private.hpp).
*
* @return The name of the Objective C type associated with the class.
*
* @see get_cpp_proxy function in DJICppWrapperCache+Private.hpp
*/
virtual const std::string objcProxyClassName() { return "TXSTextboxListener"; }

virtual void update(const ItemList & items) = 0;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ protected void finalize() throws java.lang.Throwable
super.finalize();
}

// SortItems methods

@Override
public void sort(SortOrder order, ItemList items)
{
Expand Down
9 changes: 8 additions & 1 deletion example/generated-src/objc/TXSSortItems+Private.mm
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ - (id)initWithCpp:(const std::shared_ptr<::textsort::SortItems>&)cppRef
return self;
}

- (const std::shared_ptr<::textsort::SortItems>&) cppRef
{
return _cppRefHandle.get();
}

// TXSSortItems methods

- (void)sort:(TXSSortOrder)order
items:(nonnull TXSItemList *)items {
try {
Expand Down Expand Up @@ -61,7 +68,7 @@ + (nonnull TXSItemList *)runSort:(nonnull TXSItemList *)items {
if (!objc) {
return nullptr;
}
return objc->_cppRefHandle.get();
return [objc cppRef];
}

auto SortItems::fromCppOpt(const CppOptType& cpp) -> ObjcType
Expand Down
2 changes: 2 additions & 0 deletions example/generated-src/objc/TXSTextboxListener+Private.mm
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
{
public:
using Handle::Handle;

// TextboxListener methods
void update(const ::textsort::ItemList & c_items) override
{
@autoreleasepool {
Expand Down
2 changes: 1 addition & 1 deletion example/generated-src/objc/TXSTextboxListener.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#import <Foundation/Foundation.h>


@protocol TXSTextboxListener
@protocol TXSTextboxListener <NSObject>

- (void)update:(nonnull TXSItemList *)items;

Expand Down
5 changes: 4 additions & 1 deletion example/objc/TextSort.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = TXS;
LastUpgradeCheck = 0510;
LastUpgradeCheck = 0730;
ORGANIZATIONNAME = "Dropbox, Inc.";
};
buildConfigurationList = 65834DD819AC599E0061AD3F /* Build configuration list for PBXProject "TextSort" */;
Expand Down Expand Up @@ -236,6 +236,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
Expand Down Expand Up @@ -301,6 +302,7 @@
GCC_PREFIX_HEADER = "TextSort/TextSort-Prefix.pch";
INFOPLIST_FILE = "TextSort/TextSort-Info.plist";
LIBRARY_SEARCH_PATHS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = "Dropbox.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = 1;
WRAPPER_EXTENSION = app;
Expand All @@ -316,6 +318,7 @@
GCC_PREFIX_HEADER = "TextSort/TextSort-Prefix.pch";
INFOPLIST_FILE = "TextSort/TextSort-Info.plist";
LIBRARY_SEARCH_PATHS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = "Dropbox.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = 1;
WRAPPER_EXTENSION = app;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand All @@ -23,10 +23,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
Expand All @@ -38,17 +38,21 @@
ReferencedContainer = "container:TextSort.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "65834DDC19AC599E0061AD3F"
Expand All @@ -61,12 +65,13 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "65834DDC19AC599E0061AD3F"
Expand Down
2 changes: 1 addition & 1 deletion example/objc/TextSort/TextSort-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>Dropbox.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
Expand Down
68 changes: 59 additions & 9 deletions src/source/CppGenerator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,36 +27,42 @@ import scala.collection.mutable
class CppGenerator(spec: Spec) extends Generator(spec) {

val marshal = new CppMarshal(spec)
val jniMarshal = new JNIMarshal(spec)
val objcMarshal = new ObjcMarshal(spec)

val writeCppFile = writeCppFileGeneric(spec.cppOutFolder.get, spec.cppNamespace, spec.cppFileIdentStyle, spec.cppIncludePrefix) _
def writeHppFile(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit = (w => {})) =
writeHppFileGeneric(spec.cppHeaderOutFolder.get, spec.cppNamespace, spec.cppFileIdentStyle)(name, origin, includes, fwds, f, f2)

class CppRefs(name: String) {
class CppRefs(ident: Ident) {
var hpp = mutable.TreeSet[String]()
var hppFwds = mutable.TreeSet[String]()
var cpp = mutable.TreeSet[String]()

def addInclude(ident: Ident) {
hpp.add(s"#include ${marshal.include(ident)}")
}

def find(ty: TypeRef, forwardDeclareOnly: Boolean) { find(ty.resolved, forwardDeclareOnly) }
def find(tm: MExpr, forwardDeclareOnly: Boolean) {
tm.args.foreach((x) => find(x, forwardDeclareOnly))
find(tm.base, forwardDeclareOnly)
}
def find(m: Meta, forwardDeclareOnly : Boolean) = {
for(r <- marshal.hppReferences(m, name, forwardDeclareOnly)) r match {
for(r <- marshal.hppReferences(m, ident.name, forwardDeclareOnly)) r match {
case ImportRef(arg) => hpp.add("#include " + arg)
case DeclRef(decl, Some(spec.cppNamespace)) => hppFwds.add(decl)
case DeclRef(_, _) =>
}
for(r <- marshal.cppReferences(m, name, forwardDeclareOnly)) r match {
for(r <- marshal.cppReferences(m, ident.name, forwardDeclareOnly)) r match {
case ImportRef(arg) => cpp.add("#include " + arg)
case DeclRef(_, _) =>
}
}
}

override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) {
val refs = new CppRefs(ident.name)
val refs = new CppRefs(ident)
val self = marshal.typename(ident, e)

if (spec.cppEnumHashWorkaround) {
Expand Down Expand Up @@ -135,7 +141,7 @@ class CppGenerator(spec: Spec) extends Generator(spec) {
}

override def generateRecord(origin: String, ident: Ident, doc: Doc, params: Seq[TypeParam], r: Record) {
val refs = new CppRefs(ident.name)
val refs = new CppRefs(ident)
r.fields.foreach(f => refs.find(f.ty, false))
r.consts.foreach(c => refs.find(c.ty, false))
refs.hpp.add("#include <utility>") // Add for std::move
Expand Down Expand Up @@ -262,8 +268,14 @@ class CppGenerator(spec: Spec) extends Generator(spec) {

}

override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) {
val refs = new CppRefs(ident.name)
override def generateInterface(idl: Seq[TypeDecl], origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) {
val refs = new CppRefs(ident)
if (i.superIdent.isDefined) {
refs.addInclude(i.superIdent.get)
}

refs.hpp.add("#include <string>") // needed for std::string jniProxyClassName();

i.methods.map(m => {
m.params.map(p => refs.find(p.ty, true))
m.ret.foreach((x)=>refs.find(x, true))
Expand All @@ -273,15 +285,54 @@ class CppGenerator(spec: Spec) extends Generator(spec) {
})

val self = marshal.typename(ident, i)
val superNametype = marshal.superTypename(i)
val methodNamesInScope = i.methods.map(m => idCpp.method(m.ident))

writeHppFile(ident, origin, refs.hpp, refs.hppFwds, w => {

writeDoc(w, doc)
writeCppTypeParams(w, typeParams)
w.w(s"class $self").bracedSemi {

val extendsDef = if (superNametype.isDefined) " : public " + superNametype.get else ""
w.w(s"class $self$extendsDef").bracedSemi {
w.wlOutdent("public:")
// Destructor
w.wl(s"virtual ~$self() {}")

// Proxy class name
val jniProxyClassName = if (i.ext.cpp) q(jniMarshal.undecoratedTypename(ident, i) + "$CppProxy") else "nullptr"
w.wl
w.wl("/**")
w.wl(" * Defines the name of the JNI C++ proxy class. Used to convert a")
w.wl(" * C++ implemented object to a Java object when the type of the object being")
w.wl(" * converted is unknown to the JniInterface (see djinni_support.hpp).")
w.wl(" * ")
w.wl(" * The proxy class name is only used for converting Djinni interfaces that")
w.wl(" * are implemented in C++. Java implemented interfaces are converted differently.")
w.wl(" * However, the C++ class of an interface implemented in Java must still have a")
w.wl(" * jniProxyClassName method in order for Djinni's JniInterface::fromCpp method to compile.")
w.wl(" * ")
w.wl(" * @return The name of the class's associated JNI proxy class.")
w.wl(" * ")
w.wl(" * @see JniInterface in djinni_support.hpp")
w.wl(" */")
w.wl(s"virtual const std::string jniProxyClassName() { return $jniProxyClassName; }")

val objcTypeName = objcMarshal.typename(ident, i)
val objcProxyClassName = q(if (i.ext.objc && i.ext.cpp) objcTypeName + "CppProxy" else objcTypeName)

w.wl
w.wl("/**")
w.wl(" * Defines the name of the Objective-C type for the class. Used to convert a")
w.wl(" * C++ object to an Objective-C object when the type of the object being")
w.wl(" * converted is unknown to the C++ wrapper cache (see DJICppWrapperCache+Private.hpp).")
w.wl(" * ")
w.wl(" * @return The name of the Objective C type associated with the class.")
w.wl(" * ")
w.wl(" * @see get_cpp_proxy function in DJICppWrapperCache+Private.hpp")
w.wl(" */")
w.wl(s"virtual const std::string objcProxyClassName() { return $objcProxyClassName; }")

// Constants
generateHppConstants(w, i.consts)
// Methods
Expand Down Expand Up @@ -313,5 +364,4 @@ class CppGenerator(spec: Spec) extends Generator(spec) {
if (params.isEmpty) return
w.wl("template " + params.map(p => "typename " + idCpp.typeParam(p.ident)).mkString("<", ", ", ">"))
}

}
5 changes: 5 additions & 0 deletions src/source/CppMarshal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ class CppMarshal(spec: Spec) extends Marshal(spec) {
case r: Record => withNs(Some(spec.cppNamespace), idCpp.ty(name))
}

def superTypename(ty: TypeDef): Option[String] = ty match {
case i: Interface => if (i.superIdent.isDefined) Some(idCpp.ty(i.superIdent.get.name)) else None
case _ => None
}

def paramType(tm: MExpr, scopeSymbols: Seq[String]): String = toCppParamType(tm, None, scopeSymbols)
def paramType(ty: TypeRef, scopeSymbols: Seq[String]): String = paramType(ty.resolved, scopeSymbols)
override def paramType(tm: MExpr): String = toCppParamType(tm)
Expand Down
Loading