Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions include/mp/proxy-types.h
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,19 @@ decltype(auto) CustomReadField(TypeList<LocalType> param,
return read_dest.update([&](auto& value) { ReadOne<0>(param, invoke_context, input, value); });
}

//! Overload CustomReadField to serialize objects that have CustomReadMessage
//! overloads. Defining a CustomReadMessage overload is simpler than defining a
//! CustomReadField overload because it only requires defining a normal
//! function, not a template function, but less flexible.
template <typename LocalType, typename Reader, typename ReadDest>
decltype(auto) CustomReadField(TypeList<LocalType>, Priority<2>, InvokeContext& invoke_context, Reader&& reader,
ReadDest&& read_dest,
decltype(CustomReadMessage(invoke_context, reader.get(),
std::declval<LocalType&>()))* enable = nullptr)
{
return read_dest.update([&](auto& value) { if (reader.has()) CustomReadMessage(invoke_context, reader.get(), value); });
}

template <typename... LocalTypes, typename... Args>
decltype(auto) ReadField(TypeList<LocalTypes...>, Args&&... args)
{
Expand Down Expand Up @@ -719,6 +732,17 @@ bool CustomHasValue(InvokeContext& invoke_context, Values&&... value)
return true;
}

//! Overload CustomBuildField to serialize objects that have CustomBuildMessage
//! overloads. Defining a CustomBuildMessage overload is simpler than defining a
//! CustomBuildField overload because it only requires defining a normal
//! function, not a template function, but less flexible.
template <typename LocalType, typename Value, typename Output>
void CustomBuildField(TypeList<LocalType>, Priority<2>, InvokeContext& invoke_context, Value&& value, Output&& output,
decltype(CustomBuildMessage(invoke_context, value, std::move(output.get())))* enable = nullptr)
{
CustomBuildMessage(invoke_context, value, std::move(output.init()));
}

template <typename... LocalTypes, typename Context, typename... Values, typename Output>
void BuildField(TypeList<LocalTypes...>, Context& context, Output&& output, Values&&... values)
{
Expand Down Expand Up @@ -1389,10 +1413,61 @@ struct ServerExcept : Parent
}
};

//! Helper for CustomPassField below. Call Accessor::get method if it has one,
//! otherwise return capnp::Void.
template <typename Accessor, typename Message>
decltype(auto) MaybeGet(Message&& message, decltype(Accessor::get(message))* enable = nullptr)
{
return Accessor::get(message);
}

template <typename Accessor>
::capnp::Void MaybeGet(...)
{
return {};
}

//! Helper for CustomPassField below. Call Accessor::init method if it has one,
//! otherwise do nothing.
template <typename Accessor, typename Message>
decltype(auto) MaybeInit(Message&& message, decltype(Accessor::get(message))* enable = nullptr)
{
return Accessor::init(message);
}

template <typename Accessor>
::capnp::Void MaybeInit(...)
{
return {};
}

//! Overload CustomPassField to serialize objects that have CustomPassMessage
//! overloads. Defining a CustomPassMessage overload is simpler than defining a
//! CustomPassField overload because it only requires defining a normal
//! function, not a template function, but less flexible.
template <typename Accessor, typename... LocalTypes, typename ServerContext, typename Fn, typename... Args>
auto CustomPassField(TypeList<LocalTypes...>, ServerContext& server_context, Fn&& fn, Args&&... args)
-> decltype(CustomPassMessage(server_context, MaybeGet<Accessor>(server_context.call_context.getParams()),
MaybeGet<Accessor>(server_context.call_context.getResults()), nullptr))
{
CustomPassMessage(server_context, MaybeGet<Accessor>(server_context.call_context.getParams()),
MaybeInit<Accessor>(server_context.call_context.getResults()),
[&](LocalTypes... param) { fn.invoke(server_context, std::forward<Args>(args)..., param...); });
}

template <class Accessor>
void CustomPassField();

//! PassField override calling CustomPassField function, if it exists.
//! Defining a CustomPassField or CustomPassMessage overload is useful for
//! input/output parameters. If an overload is not defined these parameters will
//! just be deserialized on the server side with ReadField into a temporary
//! variable, then the server method will be called passing the temporary
//! variable as a parameter, then the temporary variable will be serialized and
//! sent back to the client with BuildField. But if a PassField or PassMessage
//! overload is defined, the overload is called with a callback to invoke and
//! pass parameters to the server side function, and run arbitrary code before
//! and after invoking the function.
template <typename Accessor, typename... Args>
auto PassField(Priority<2>, Args&&... args) -> decltype(CustomPassField<Accessor>(std::forward<Args>(args)...))
{
Expand Down
39 changes: 39 additions & 0 deletions test/mp/test/foo-types.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,45 @@ decltype(auto) CustomReadField(TypeList<FooCustom>, Priority<1>, InvokeContext&
}

} // namespace test

inline void CustomBuildMessage(InvokeContext& invoke_context,
const test::FooMessage& src,
test::messages::FooMessage::Builder&& builder)
{
builder.setMessage(src.message + " build");
}

inline void CustomReadMessage(InvokeContext& invoke_context,
const test::messages::FooMessage::Reader& reader,
test::FooMessage& dest)
{
dest.message = std::string{reader.getMessage()} + " read";
}

inline void CustomBuildMessage(InvokeContext& invoke_context,
const test::FooMutable& src,
test::messages::FooMutable::Builder&& builder)
{
builder.setMessage(src.message + " build");
}

inline void CustomReadMessage(InvokeContext& invoke_context,
const test::messages::FooMutable::Reader& reader,
test::FooMutable& dest)
{
dest.message = std::string{reader.getMessage()} + " read";
}

inline void CustomPassMessage(InvokeContext& invoke_context,
const test::messages::FooMutable::Reader& reader,
test::messages::FooMutable::Builder builder,
std::function<void(test::FooMutable&)>&& fn)
{
test::FooMutable mut;
mut.message = std::string{reader.getMessage()} + " pass";
fn(mut);
builder.setMessage(mut.message + " return");
}
} // namespace mp

#endif // MP_TEST_FOO_TYPES_H
10 changes: 10 additions & 0 deletions test/mp/test/foo.capnp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ interface FooInterface $Proxy.wrap("mp::test::FooImplementation") {
callbackExtended @10 (context :Proxy.Context, callback :ExtendedCallback, arg: Int32) -> (result :Int32);
passCustom @11 (arg :FooCustom) -> (result :FooCustom);
passEmpty @12 (arg :FooEmpty) -> (result :FooEmpty);
passMessage @13 (arg :FooMessage) -> (result :FooMessage);
passMutable @14 (arg :FooMutable) -> (arg :FooMutable);
}

interface FooCallback $Proxy.wrap("mp::test::FooCallback") {
Expand All @@ -50,6 +52,14 @@ struct FooCustom $Proxy.wrap("mp::test::FooCustom") {
struct FooEmpty $Proxy.wrap("mp::test::FooEmpty") {
}

struct FooMessage {
message @0 :Text;
}

struct FooMutable {
message @0 :Text;
}

struct Pair(T1, T2) {
first @0 :T1;
second @1 :T2;
Expand Down
12 changes: 12 additions & 0 deletions test/mp/test/foo.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ struct FooEmpty
{
};

struct FooMessage
{
std::string message;
};

struct FooMutable
{
std::string message;
};

class FooCallback
{
public:
Expand Down Expand Up @@ -60,6 +70,8 @@ class FooImplementation
int callbackExtended(ExtendedCallback& callback, int arg) { return callback.callExtended(arg); }
FooCustom passCustom(FooCustom foo) { return foo; }
FooEmpty passEmpty(FooEmpty foo) { return foo; }
FooMessage passMessage(FooMessage foo) { foo.message += " call"; return foo; }
void passMutable(FooMutable& foo) { foo.message += " call"; }
std::shared_ptr<FooCallback> m_callback;
};

Expand Down
10 changes: 10 additions & 0 deletions test/mp/test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ KJ_TEST("Call FooInterface methods")

foo->passEmpty(FooEmpty{});

FooMessage message1;
message1.message = "init";
FooMessage message2{foo->passMessage(message1)};
KJ_EXPECT(message2.message == "init build read call build read");

FooMutable mut;
mut.message = "init";
foo->passMutable(mut);
KJ_EXPECT(mut.message == "init build pass call return read");

disconnect_client();
thread.join();

Expand Down