diff --git a/src/ipc/capnp/common-types.h b/src/ipc/capnp/common-types.h index 302da76c055..f1e2d18f1ce 100644 --- a/src/ipc/capnp/common-types.h +++ b/src/ipc/capnp/common-types.h @@ -112,6 +112,33 @@ decltype(auto) CustomReadField(TypeList, Priority<1>, InvokeContext& i value.read(std::string_view{data.begin(), data.size()}); }); } + +//! Generic ::capnp::Data field builder for any C++ type that can be converted +//! to a span of bytes, like std::vector or std::array, or custom +//! blob types like uint256 or PKHash with data() and size() methods pointing to +//! bytes. +//! +//! Note: it might make sense to move this function into libmultiprocess, since +//! it is fairly generic. However this would require decreasing its priority so +//! it can be overridden, which would require more changes inside +//! libmultiprocess to avoid conflicting with the Priority<1> CustomBuildField +//! function it already provides for std::vector. Also, it might make sense to +//! provide a CustomReadField counterpart to this function, which could be +//! called to read C++ types that can be constructed from spans of bytes from +//! ::capnp::Data fields. But so far there hasn't been a need for this. +template +void CustomBuildField(TypeList, Priority<2>, InvokeContext& invoke_context, Value&& value, Output&& output) +requires + (std::is_same_v) && + (std::convertible_to> || + std::convertible_to> || + std::convertible_to> || + std::convertible_to>) +{ + auto data = std::span{value}; + auto result = output.init(data.size()); + memcpy(result.begin(), data.data(), data.size()); +} } // namespace mp #endif // BITCOIN_IPC_CAPNP_COMMON_TYPES_H diff --git a/src/test/ipc_test.capnp b/src/test/ipc_test.capnp index 00cfeb53215..32c01dba463 100644 --- a/src/test/ipc_test.capnp +++ b/src/test/ipc_test.capnp @@ -16,4 +16,5 @@ interface FooInterface $Proxy.wrap("FooImplementation") { passOutPoint @1 (arg :Data) -> (result :Data); passUniValue @2 (arg :Text) -> (result :Text); passTransaction @3 (arg :Data) -> (result :Data); + passVectorChar @4 (arg :Data) -> (result :Data); } diff --git a/src/test/ipc_test.cpp b/src/test/ipc_test.cpp index 59083beabc6..91afce77786 100644 --- a/src/test/ipc_test.cpp +++ b/src/test/ipc_test.cpp @@ -97,6 +97,10 @@ void IpcPipeTest() CTransactionRef tx2{foo->passTransaction(tx1)}; BOOST_CHECK(*Assert(tx1) == *Assert(tx2)); + std::vector vec1{'H', 'e', 'l', 'l', 'o'}; + std::vector vec2{foo->passVectorChar(vec1)}; + BOOST_CHECK_EQUAL(std::string_view(vec1.begin(), vec1.end()), std::string_view(vec2.begin(), vec2.end())); + // Test cleanup: disconnect pipe and join thread disconnect_client(); thread.join(); diff --git a/src/test/ipc_test.h b/src/test/ipc_test.h index 22fe96b6bac..ca8a5f61b51 100644 --- a/src/test/ipc_test.h +++ b/src/test/ipc_test.h @@ -16,6 +16,7 @@ public: COutPoint passOutPoint(COutPoint o) { return o; } UniValue passUniValue(UniValue v) { return v; } CTransactionRef passTransaction(CTransactionRef t) { return t; } + std::vector passVectorChar(std::vector v) { return v; } }; void IpcPipeTest();