mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-21 14:34:49 +01:00
multiprocess: Add type conversion code for serializable types
Allow any C++ object that has Serialize and Unserialize methods and can be serialized to a bitcoin CDataStream to be converted to a capnproto Data field and passed as arguments or return values to capnproto methods using the Data type. Extend IPC unit test to cover this and verify the serialization happens correctly.
This commit is contained in:
parent
4aaee23921
commit
0cc74fce72
5 changed files with 98 additions and 1 deletions
|
@ -1096,6 +1096,7 @@ ipc/capnp/libbitcoin_ipc_a-protocol.$(OBJEXT): $(libbitcoin_ipc_mpgen_input:=.h)
|
|||
if BUILD_MULTIPROCESS
|
||||
LIBBITCOIN_IPC=libbitcoin_ipc.a
|
||||
libbitcoin_ipc_a_SOURCES = \
|
||||
ipc/capnp/common-types.h \
|
||||
ipc/capnp/context.h \
|
||||
ipc/capnp/init-types.h \
|
||||
ipc/capnp/protocol.cpp \
|
||||
|
|
89
src/ipc/capnp/common-types.h
Normal file
89
src/ipc/capnp/common-types.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
// Copyright (c) 2023 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_IPC_CAPNP_COMMON_TYPES_H
|
||||
#define BITCOIN_IPC_CAPNP_COMMON_TYPES_H
|
||||
|
||||
#include <clientversion.h>
|
||||
#include <streams.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <mp/proxy-types.h>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace ipc {
|
||||
namespace capnp {
|
||||
//! Use SFINAE to define Serializeable<T> trait which is true if type T has a
|
||||
//! Serialize(stream) method, false otherwise.
|
||||
template <typename T>
|
||||
struct Serializable {
|
||||
private:
|
||||
template <typename C>
|
||||
static std::true_type test(decltype(std::declval<C>().Serialize(std::declval<std::nullptr_t&>()))*);
|
||||
template <typename>
|
||||
static std::false_type test(...);
|
||||
|
||||
public:
|
||||
static constexpr bool value = decltype(test<T>(nullptr))::value;
|
||||
};
|
||||
|
||||
//! Use SFINAE to define Unserializeable<T> trait which is true if type T has
|
||||
//! an Unserialize(stream) method, false otherwise.
|
||||
template <typename T>
|
||||
struct Unserializable {
|
||||
private:
|
||||
template <typename C>
|
||||
static std::true_type test(decltype(std::declval<C>().Unserialize(std::declval<std::nullptr_t&>()))*);
|
||||
template <typename>
|
||||
static std::false_type test(...);
|
||||
|
||||
public:
|
||||
static constexpr bool value = decltype(test<T>(nullptr))::value;
|
||||
};
|
||||
} // namespace capnp
|
||||
} // namespace ipc
|
||||
|
||||
//! Functions to serialize / deserialize common bitcoin types.
|
||||
namespace mp {
|
||||
//! Overload multiprocess library's CustomBuildField hook to allow any
|
||||
//! serializable object to be stored in a capnproto Data field or passed to a
|
||||
//! canproto interface. Use Priority<1> so this hook has medium priority, and
|
||||
//! higher priority hooks could take precedence over this one.
|
||||
template <typename LocalType, typename Value, typename Output>
|
||||
void CustomBuildField(
|
||||
TypeList<LocalType>, Priority<1>, InvokeContext& invoke_context, Value&& value, Output&& output,
|
||||
// Enable if serializeable and if LocalType is not cv or reference
|
||||
// qualified. If LocalType is cv or reference qualified, it is important to
|
||||
// fall back to lower-priority Priority<0> implementation of this function
|
||||
// that strips cv references, to prevent this CustomBuildField overload from
|
||||
// taking precedence over more narrow overloads for specific LocalTypes.
|
||||
std::enable_if_t<ipc::capnp::Serializable<LocalType>::value &&
|
||||
std::is_same_v<LocalType, std::remove_cv_t<std::remove_reference_t<LocalType>>>>* enable = nullptr)
|
||||
{
|
||||
DataStream stream;
|
||||
value.Serialize(stream);
|
||||
auto result = output.init(stream.size());
|
||||
memcpy(result.begin(), stream.data(), stream.size());
|
||||
}
|
||||
|
||||
//! Overload multiprocess library's CustomReadField hook to allow any object
|
||||
//! with an Unserialize method to be read from a capnproto Data field or
|
||||
//! returned from canproto interface. Use Priority<1> so this hook has medium
|
||||
//! priority, and higher priority hooks could take precedence over this one.
|
||||
template <typename LocalType, typename Input, typename ReadDest>
|
||||
decltype(auto)
|
||||
CustomReadField(TypeList<LocalType>, Priority<1>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest,
|
||||
std::enable_if_t<ipc::capnp::Unserializable<LocalType>::value>* enable = nullptr)
|
||||
{
|
||||
return read_dest.update([&](auto& value) {
|
||||
if (!input.has()) return;
|
||||
auto data = input.get();
|
||||
SpanReader stream({data.begin(), data.end()});
|
||||
value.Unserialize(stream);
|
||||
});
|
||||
}
|
||||
} // namespace mp
|
||||
|
||||
#endif // BITCOIN_IPC_CAPNP_COMMON_TYPES_H
|
|
@ -9,7 +9,9 @@ $Cxx.namespace("gen");
|
|||
|
||||
using Proxy = import "/mp/proxy.capnp";
|
||||
$Proxy.include("test/ipc_test.h");
|
||||
$Proxy.includeTypes("ipc/capnp/common-types.h");
|
||||
|
||||
interface FooInterface $Proxy.wrap("FooImplementation") {
|
||||
add @0 (a :Int32, b :Int32) -> (result :Int32);
|
||||
passOutPoint @1 (arg :Data) -> (result :Data);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <mp/proxy-types.h>
|
||||
#include <logging.h>
|
||||
#include <mp/proxy-types.h>
|
||||
#include <test/ipc_test.capnp.h>
|
||||
#include <test/ipc_test.capnp.proxy.h>
|
||||
#include <test/ipc_test.h>
|
||||
|
@ -51,6 +51,10 @@ void IpcTest()
|
|||
// Test: make sure arguments were sent and return value is received
|
||||
BOOST_CHECK_EQUAL(foo->add(1, 2), 3);
|
||||
|
||||
COutPoint txout1{Txid::FromUint256(uint256{100}), 200};
|
||||
COutPoint txout2{foo->passOutPoint(txout1)};
|
||||
BOOST_CHECK(txout1 == txout2);
|
||||
|
||||
// Test cleanup: disconnect pipe and join thread
|
||||
disconnect_client();
|
||||
thread.join();
|
||||
|
|
|
@ -11,6 +11,7 @@ class FooImplementation
|
|||
{
|
||||
public:
|
||||
int add(int a, int b) { return a + b; }
|
||||
COutPoint passOutPoint(COutPoint o) { return o; }
|
||||
};
|
||||
|
||||
void IpcTest();
|
||||
|
|
Loading…
Add table
Reference in a new issue