You raise a great point about the CodeGeneratorRequest being "incomplete" 
and also not knowing the type ID of the held capability, which does 
complicate things. That said, I'm not convinced about your data store use 
case. It doesn't seem like something that needs to be integral to the capnp 
protocol, since there's already a shared schema between the peers. For 
example, it seems to me that your server interface would probably just take 
a Struct's type ID, and fail to set it if the type ID was unknown. Then 
provide a separate method to register a schema, which the client can use. A 
similar process would apply for StrudyRefs/Provided capabilities in levels 
2/3.

```
enum ErrorCode {
  unknownSchema @0;
}

interface DataStore {
  setStruct @0 (obj :AnyPointer, type :UInt64) -> (err :ErrorCode);
  registerSchemas @1 (schemas :List(CodeGeneratorRequest)) -> ();
}
```

When you are coming at the problem with no shared schema (only the capnp 
rpc protocol), there isn't (currently) any way to get the schema at all, 
since you can't send a meaningful request to the peer to get it.

```
interface Introspectable {
  supportedInterfaces @0 () -> (interfaces :List(UInt64);
  schemaForTypes @1 (types :List(UInt64)) -> (schemas 
:List(CodeGeneratorRequest));
}
```

I've dropped the second interface since, as far as I can tell, the only 
place this needs to be implemented is again on the root object from 
Bootstrap/Accept. I've kept the List(UInt64), but switched to a multi-get 
for the schema fetcher, which has the nice advantage of allowing pipelining 
if you believe ahead of time you won't have the schema for the returned 
interfaces.

While it does feel a little weird to attach a global "schemaForTypes" 
method to any arbitrary capability, it ends up being necessary in the case 
of capability proxies; and the only alternative would be to implement it as 
part of the RPC protocol. That would be a root-level message to request 
schemas, which means that capability proxies now also need to be aware of 
the schema of the capability they hold.
On Wednesday, September 16, 2020 at 1:11:46 AM UTC+8 [email protected] wrote:

> I was actually thinking about reflection like this this morning, though
> my use case is a bit different: I'm building a data store on top of
> capnproto, and would like to be able to introspect the stored objects
> without a-priori knowing their schema. This probably means needing to
> store the schema, so the client needs to provide the server with the cgr
> when the data is stored. But it seems wasteful to have to attach the cgr
> to each object, so some protocol is needed for the server to say to the
> client, "hey, I don't know about this type yet?"
>
> Quoting Ryan Patterson (2020-09-15 07:52:11)
>
> > Here is my straw man version of this interface. I've selected
> > AnyPointer so that implementing this interface doesn't require pulling
> > the entire capnp schema into your own schema. Since the capnp schema is
> > known to both parties (even if it's a different version), this is fine.
> > interface Introspectable {
> > codeGeneratorRequest @0 () -> (cgr :AnyPointer);
> > }
>
> A couple thoughts:
>
> * I'm not 100% sure I understand the problem you're trying to solve by
> using AnyPointer. Is it just avoiding code bloat due to having to pull
> in the declarations for CodeGeneratorRequest? Maybe Kenton can shed
> some more light, but my understanding is that (for the C++
> implementation anyway) the generated code is mostly inline accessors,
> so I'm not sure how important that is? But perhaps it would more
> adversely affect other implementations.
> * I can think of a few things you probably would want to do that this
> interface can't handle:
> * Asking what interfaces the object supports; the above is enough to
> introspect messages that cross the wire since they include the
> interface ID, but it seems natural to also want to enumerate the
> available interfaces.
> * Fetching dependencies of a CodeGeneratorRequest; In general, the cgr
> will only include information for the schema for which code was
> generated; if that schema imported other (separately compiled)
> schema, there may be dangling references to types not defined in the
> cgr.
>
> Here's another stab:
>
> ```
> interface SchemaRegistry {
> cgrForType(typeId :UInt64) -> (cgr :CodeGeneratorRequest);
> # Returns the cgr containing the given type Id.
> }
>
> interface Introspectable extends SchemaRegistry {
> supportedInterfaces @1 () -> (interfaces :List(UInt64);
> # Returns a list of the interface ids that this object supports.
> # superclasses can be omitted as they are implied.
> }
> ```
>
> I believe that supports the use cases outlined so far. I split
> SchemaRegistry out as a separate type so it's possible to query
> information about types that aren't interfaces, as in my data store
> use case.
>
> Note that for the C++ implementation, there'd be no reason for
> supportedInterfaces() to return a list, since you need to have a common
> subtype at export time anyway, but this isn't true of all
> implementations; at least the Go implementation lets you put method
> sets together at runtime.
>
> The above could use some improvement in that it might take several round
> trips to get what you want, but my brain is moving slowly so I'm to go
> get some caffeine instead of trying to make it perfect on this iteration.
>
> -Ian
>

-- 
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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/capnproto/9faf97f6-bf67-4db4-958b-55a57492e584n%40googlegroups.com.

Reply via email to