I am using Djinni to share some big c++ codebase among android and ios. One of the various components (let's call it Foo
!!!) has a different implementation on android and ios. Foo
is an interface, which the c++ code uses without the need of knowing anything about its internals.
The android implementation though (FooAndroid
), has some additional methods that the android client can use to modify the behaviour of the component in ways only meaningful to the android platform.
This complicates things with djinni, because of the lack of interface inheritance. I came up with a solution of sorts, that relies on the fact that FooAndroid
can subclass two different djinni interfaces that have most of their methods with identical signature, but the result is not pretty.
This is the djinni interface description:
foo = interface +c {
static create() : foo;
doSomething();
}
foo_android = interface +c {
static create() : foo_android;
doSomething();
doAnotherThing();
asFoo(): foo;
}
bar = interface +c {
static create() : bar;
useFoo(f: foo);
}
You'll notice Bar
a component that just needs to access a Foo
instance.
The resulting interfaces are then implemented in this way:
// Foo android implementation:
class FooAndroidImpl : public Foo, public FooAndroid {
public:
void doSomething() override { LOGI("do something"); }
void doAnotherThing() override { LOGI(" do something"); }
std::weak_ptr<Api::Foo> fooSelfReference;
std::shared_ptr<Api::Foo> asFoo() override { return fooSelfRef.lock(); }
};
// instance creation methods:
std::shared_ptr<FooAndroidImpl> createImpl() {
auto p = std::make_shared<FooAndroidImpl>();
p->fooSelfRef = p;
return p;
}
std::shared_ptr<FooAndroid> FooAndroid::create()
{
return createImpl();
}
std::shared_ptr<Foo> Foo::create()
{
return createImpl();
}
// Bar implementation:
class BarImpl : public Bar {
public:
void useFoo(const std::shared_ptr<Foo> & f) override { f->doSomething(); }
};
std::shared_ptr<Bar> Bar::create()
{
return std::make_shared<BarImpl>();
}
I am then able to use FooAndroid and Bar in java in this way:
FooAndroid fooAndroid = FooAndroid.create();
fooAndroid.doAnotherThing();
Bar bar = Bar.create();
bar.useFoo(fooAndroid.asFoo());
This works, but it's ugly, I have to define two almost identical djinni interfaces by hand, and I am not still sure of all the implications of storing that weak_ptr on the lifecycle of the object.
I feel I am abusing Djinni's purpose and model, so maybe there's a better way to achieve what I am trying to do?