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