Best practice for dynamic_cast equivalent method for extended interfaces

55 views
Skip to first unread message

Christophe Alexandre

unread,
Oct 7, 2022, 6:05:02 AM10/7/22
to Cap'n Proto
Hi,
Sorry for the newbie question.

I've read the following thread and this is related to the same kind of question.
https://groups.google.com/g/capnproto/c/XLo5RPLpVBg/m/LI_sGi72AgAJ

With following schema example:
interface Abstract {}
interface ConcreteA extends(Abstract) {}
interface ConcreteB extends(Abstract) {}
interface Object {
  getAbstracts @0 () -> (abstracts :List(Abstract));
}

When implementing getAbstracts on the server side, only ConcreteA or ConcreteB objects are constructed and returned.
My question is: on the client side, what is the best practice for determining the Abstract object concrete type and casting it to ConcreteA or ConcreteB. Something equivalent to C++ dynamic_cast.

Thanks !
Christophe Alexandre

Kenton Varda

unread,
Oct 7, 2022, 4:42:04 PM10/7/22
to Christophe Alexandre, Cap'n Proto
Hi Christophe,

There is no built-in mechanism for this. Instead, you have to build this into your interface.

Note that a `Client` object can be "safely" cast to any type using `client.castAs<SomeInterfaceType>()`. I say "safely" meaning: you won't violate C++ memory safety by this cast, even if the server doesn't actually implement the type. Instead, if you cast to the wrong type, method calls on that type will fail with UNIMPLEMENTED exceptions. The exception is produced on the server side; the client side does not actually know the destination type so cannot predict whether the method is supported.

To that end, one way to query the type would be to simply try to call a method on `ConcreteA` or `ConcreteB` and see if it fails with `exception.getType() == kj::Exception::Type::UNIMPLEMENTED`.

But if you'd like to avoid the round trip, then I would suggest that `getAbstracts()` should return a list that contains not just the interface pointers, but also associated metadata identifying what type it is.

-Kenton

--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/capnproto/1613eaaf-6025-4eb0-90c3-a7f4a98a2e21n%40googlegroups.com.

Christophe Alexandre

unread,
Oct 8, 2022, 9:26:42 AM10/8/22
to Cap'n Proto
Thanks a lot for your very clear answer.

Christophe

Christophe Alexandre

unread,
Oct 10, 2022, 6:41:40 AM10/10/22
to Cap'n Proto
Hi,
Related to this, I tried the following schema:

interface MyType {
  getName @0 () -> (name :Text);
}
struct ObjectInfo {
  object @0 :MyType;
  isA    @1 :Bool;
}
interface Test {
  getObjectInfo @0 () -> (objectInfo: ObjectInfo);
}

After generating the C++ classes, on the client side, there is a getObject() method implemented on the ObjectInfo class but no getIsA(). 

auto request = testClient.getObjectInfoRequest();
auto promise = request.send();
auto objectInfo = promise.getObjectInfo();
auto object = objectInfo.getObject();
bool isA = objectInfo.getIsA(); //not implemented

Is it because of the Bool type ?

Thanks !
Christophe

Kenton Varda

unread,
Oct 14, 2022, 4:48:28 PM10/14/22
to Christophe Alexandre, Cap'n Proto
Hi Christophe,

The problem is here:

On Mon, Oct 10, 2022 at 5:41 AM Christophe Alexandre <christophe...@zellij.io> wrote:
auto objectInfo = promise.getObjectInfo(); 
auto object = objectInfo.getObject();

Note that you are operating on a promise here. The RPC has not actually completed yet. The `object` you have obtained here is a promised future object. You can start calling methods on it before the original call has actually completed. This is called "promise pipeline" -- or, jokingly, "time travel", on the web site. https://capnproto.org/rpc.html
 
bool isA = objectInfo.getIsA(); //not implemented

This part doesn't work because pipelining on a boolean value is not supported. Instead, you need to wait for the promise to complete, and then you can access the boolean value on the result:

auto promise2 = promise.then([](auto response) {
  bool isA = response.getObjectInfo().getIsA();
});

Or, if you have a WaitScope:

auto response = promise.wait(waitScope);
bool isA = response.getObjectInfo().getIsA();

-Kenton
 

Christophe Alexandre

unread,
Oct 16, 2022, 6:51:16 AM10/16/22
to Cap'n Proto
Thanks again Kenton !

One step at a time. I'm now falling in the trap described here: https://groups.google.com/g/capnproto/c/XLo5RPLpVBg/m/LI_sGi72AgAJ
I was wondering if there is a full C++ implementation example of a schema containing inheritance such as the Calculator example.
Thanks !
Christophe

Christophe Alexandre

unread,
Oct 17, 2022, 1:07:51 PM10/17/22
to Cap'n Proto
Thanks again Ken !

One step at a time...  Seems I'm now falling in the trap described here: https://groups.google.com/g/capnproto/c/9J_AOQzSEbU/m/qpuEjQXhBAAJ
So I'm wondering now if there is an available example of C++ implementation of a RPC schema using inheritance, similar to the Calculator example.

Thanks
Christophe
Reply all
Reply to author
Forward
0 new messages