Copying existing struct into request params

57 views
Skip to first unread message

Vaci

unread,
Jun 2, 2021, 7:28:58 AM6/2/21
to Cap'n Proto
I may be missing something obvious, but is it possible to copy the content of a struct into the body of a request builder?

I have an interface of the form:

   struct Foo {};

  interface Bar {
     foo @0 Foo;
  };

I want to import a bunch of Foo structures using the json reader and use them to populate the requests. This works fine with a single item, because I can just decode directly into the request body:

auto foo = bar.fooRequest();
capnp::JsonCodec json;
json.decode(txt, foo);

...but if my input is a list of items, the json decoder will return an (orphaned) list of Foo structures, and I can't see a way to use each item as request params.

Vaci







Kenton Varda

unread,
Jun 2, 2021, 10:36:55 AM6/2/21
to Vaci, Cap'n Proto
Hi Vaci,

An RPC request is always a struct type, not a list, so I assume what you really mean here is that you want to use the list as a field of the request? E.g. you have:

struct Foo {}

interface Bar {
  foo @0 (foos :List(Foo));
}

Then you would do:

auto req = bar.fooRequest();
auto orphanage = capnp::Orphanage::getForMessageContaining(req);
auto orphan = json.decode<capnp::List<Foo>>(txt, orphanage);
req.adoptFoos(kj::mv(orphan));

-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/55a2f540-cece-474f-8795-b65e15f024e1n%40googlegroups.com.

Vaci

unread,
Jun 3, 2021, 4:10:32 AM6/3/21
to Cap'n Proto
That's not quite what I want to do. Given that a request is a struct type, I want to copy or assign an existing struct to the whole request.

I can use the json decoder to write content into the request struct, but I don't see a way to do that when I'm holding an orphan pointer to a struct that is of the same type as the request.

Vaci

Vaci

unread,
Jun 3, 2021, 9:59:49 AM6/3/21
to Cap'n Proto
Ah, I figured out that I can use the layered API to decode each item in the list individually:

    Bar::Client cap;
    kj::ArrayPtr<const char> text;
    capnp::MallocMessageBuilder mb;
    capnp::JsonCodec codec;
    kj::ArrayBuilder<kj::Promise<void>> promises;

    auto value = mb.initRoot<capnp::JsonValue>();
    codec.decodeRaw(text, value);

    for (auto&& entry: value.getArray()) {
      auto request = cap.fooRequest();
      codec.decode(entry, request);
      promises.add(request.send().ignoreResult());
    }

However, I'd still be curious to know how to create a request from an existing struct, if that's at all possible!

Many thanks,
Vaci

Kenton Varda

unread,
Jun 4, 2021, 3:33:59 PM6/4/21
to Vaci, Cap'n Proto
I see. There isn't a great API for this right now. The problem is, once a struct Builder is already allocated, the struct can't be resized. But if you're trying to copy the content of another struct into it, it's possible that other struct was originally created with a newer version of the schema which had new fields defined, and so it is larger. If the data were copied from that larger struct into the smaller struct that has been allocated, the new fields would be silently dropped. I wanted to avoid that situation.

One thing you could do is manually invoke Capability::Client::typelessReuqest(), manually passing the appropriate interface and method IDs. Then you get a request where the root type is AnyPointer, which you can set to an existing struct. This avoids the problem of dropping unknown fields, since the destination struct isn't allocated until the source is known, and it is then allocated with the same size as the source.

Perhaps we should update the code generator to generate RPC client methods that can accept an existing struct Reader as input, to make this nicer.

-Kenton

Vaci

unread,
Jun 9, 2021, 6:08:12 AM6/9/21
to Cap'n Proto
Thanks for the explanation on the interaction between schema evolution and copying structures, that makes sense. 
It may not be a common requirement to want to copy over the entire request structure, rather than just initialise a member from an existing reader.

For the moment, I only care about copying content from JSON, but typelessRequests might be a nice approach for generally recording and replaying RPC calls.

Thanks,
Vaci
Reply all
Reply to author
Forward
0 new messages