Skip to content

Commit

Permalink
SW-8401 Implement pre-serialization and post-deserialization callback…
Browse files Browse the repository at this point in the history
…s for capnp types (#99)

* Provide capn_preprocess and capn_postprocess for custom code to run
before/after serialization/deserialization, respectively.
  • Loading branch information
dskyle committed Sep 26, 2018
1 parent eebd9a0 commit db28f9d
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 19 deletions.
134 changes: 117 additions & 17 deletions include/madara/knowledge/CapnAdapt.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,39 @@

namespace madara { namespace knowledge {

/**
* Default implementation of capn_preprocess. Overload to provide custom
* preprocessing prior to CapnProto serialization. Argument should be a
* const reference to your custom type (not CapnProto type). Return value
* should be a smart pointer or raw pointer to the same type.
*
* If a non-null pointer is returned, the returned object will be serialized
* instead of the object originally passed in. The returned pointer's lifetime
* will be the duration of the serialization operation. In general, use
* std::unique_ptr if the object should be deleted afterwards, raw pointer
* otherwise. In the latter case, the pointed to object must outlive the
* serialization operation. It is up to you to ensure this.
*
* The overload_priority parameter type is used to manage overload ambiguity. In
* general, use overload_priority<8> in custom overload of tris function, and
* adjust to resolve ambiguity.
**/
template<typename T>
void *capn_preprocess(const T&, overload_priority_weakest) { return nullptr; }

/**
* Default implementation of capn_postprocess. Overload to provide custom
* postprocessing following CapnProto deserialization. Argument should be a
* non-const reference to your custom type (not CapnProto type). You may
* modify the object in-place via this reference.
*
* The overload_priority parameter type is used to manage overload ambiguity. In
* general, use overload_priority<8> in custom overload of tris function, and
* adjust to resolve ambiguity.
**/
template<typename T>
void capn_postprocess(T&, overload_priority_weakest) {}

template<typename T, typename C>
struct CapnPrimitive {
using CapnType = C;
Expand Down Expand Up @@ -146,12 +179,28 @@ CapnStrList<C> MakeCapnStrList(
return {get, init, has};
}

template<typename P, typename T>
auto resolve_preprocess(const P& ptr, const T& val, overload_priority<8>) ->
decltype(ptr ? *ptr : val)
{
return ptr ? *ptr : val;
}

template<typename P, typename T>
const T& resolve_preprocess(const P&, const T& val, overload_priority_weakest)
{
return val;
}

template<typename T, typename R, typename C>
inline auto capn_set(typename C::Builder &builder,
const CapnPrimitive<T, C> &prim, const R &val) ->
enable_if_<is_numeric<R>()>
{
(builder.*(prim.set))(static_cast<T>(val));
auto p = capn_preprocess(val, select_overload());
const auto &ref = resolve_preprocess(p, val, select_overload());

(builder.*(prim.set))(static_cast<T>(ref));
}

template<typename T, typename R, typename C>
Expand All @@ -160,6 +209,7 @@ inline auto capn_get(typename C::Reader &reader,
enable_if_<is_numeric<R>()>
{
val = static_cast<R>((reader.*(prim.get))());
capn_postprocess(val, select_overload());
}

auto infer_capn_type(type<std::string>) -> capnp::Text;
Expand All @@ -169,29 +219,37 @@ inline void capn_set(typename C::Builder &builder,
const CapnString<C> &prim,
const std::string &val)
{
(builder.*(prim.set))(val);
auto p = capn_preprocess(val, select_overload());
const auto &ref = resolve_preprocess(p, val, select_overload());

(builder.*(prim.set))(ref);
}

template<typename C>
inline void capn_get(typename C::Reader &reader,
const CapnString<C> &prim, std::string &val)
{
val = (reader.*(prim.get))();
capn_postprocess(val, select_overload());
}

inline void capn_set(typename capnp::Text::Builder &builder,
const std::string &val)
{
auto p = capn_preprocess(val, select_overload());
const auto &ref = resolve_preprocess(p, val, select_overload());

char *data = builder.begin();
size_t size = std::min(val.size(), builder.size());
memcpy(data, val.data(), size);
size_t size = std::min(ref.size(), builder.size());
memcpy(data, ref.data(), size);
data[size] = '\0';
}

inline void capn_get(typename capnp::Text::Reader &reader,
std::string &val)
{
val = std::string(reader.cStr(), reader.size());
capn_postprocess(val, select_overload());
}

template<typename T,
Expand All @@ -216,9 +274,12 @@ template<typename B, typename T>
inline auto capn_set(B &builder, const T &val) ->
enable_if_<supports_for_each_member<T>::value>
{
auto p = capn_preprocess(val, select_overload());
const auto &ref = resolve_preprocess(p, val, select_overload());

for_each_member(type<T>{},
do_capn_struct_set<T, ::madara::decay_<decltype(builder)>>{
&val, &builder});
&ref, &builder});
}

template<typename T, typename R>
Expand All @@ -238,8 +299,10 @@ template<typename R, typename T>
inline auto capn_get(R &reader, T &val) ->
enable_if_<supports_for_each_member<T>::value>
{
for_each_member(type<T>{}, do_capn_struct_get<T, ::madara::decay_<decltype(reader)>>{
&val, &reader});
for_each_member(type<T>{},
do_capn_struct_get<T, ::madara::decay_<decltype(reader)>>{
&val, &reader});
capn_postprocess(val, select_overload());
}

template<typename T, typename R, typename B, typename C>
Expand Down Expand Up @@ -270,9 +333,13 @@ template<typename T, typename C, typename A>
inline void capn_set(typename C::Builder &builder,
const CapnStrList<C> &info, const std::vector<T, A> &val)
{
auto list_builder{(builder.*(info.init))(val.size())};
auto p = capn_preprocess(val, select_overload());
const auto &ref = resolve_preprocess(p, val, select_overload());

auto list_builder{(builder.*(info.init))(ref.size())};

size_t i = 0;
for (const auto & cur : val) {
for (const auto & cur : ref) {
auto elem_builder = list_builder.init(i, cur.size());
capn_set(elem_builder, cur);
++i;
Expand All @@ -285,22 +352,29 @@ inline void capn_get(typename C::Reader &reader,
{
auto list_reader{(reader.*(info.get))()};
val.resize(list_reader.size());

size_t i = 0;
for (auto cur : list_reader) {
capn_get(cur, val[i]);
++i;
}

capn_postprocess(val, select_overload());
}

template<typename T, typename R, typename B, typename C, typename A>
inline auto capn_set(typename C::Builder &builder,
const CapnList<R, B, C> &info, const std::vector<T, A> &val) ->
enable_if_<supports_capn_set<T>::value && !std::is_arithmetic<T>::value>
{
(builder.*(info.init))(val.size());
auto p = capn_preprocess(val, select_overload());
const auto &ref = resolve_preprocess(p, val, select_overload());

(builder.*(info.init))(ref.size());
auto list_builder{(builder.*(info.get_builder))()};

size_t i = 0;
for (const auto & cur : val) {
for (const auto & cur : ref) {
auto elem_builder = list_builder[i];
capn_set(elem_builder, cur);
++i;
Expand All @@ -314,22 +388,29 @@ inline auto capn_get(typename C::Reader &reader,
{
auto list_reader{(reader.*(info.get))()};
val.resize(list_reader.size());

size_t i = 0;
for (auto cur : list_reader) {
capn_get(cur, val[i]);
++i;
}

capn_postprocess(val, select_overload());
}

template<typename T, typename R, typename B, typename C, typename A>
inline auto capn_set(typename C::Builder &builder,
const CapnList<R, B, C> &info, const std::vector<T, A> &val) ->
enable_if_<std::is_arithmetic<T>::value>
{
(builder.*(info.init))(val.size());
auto p = capn_preprocess(val, select_overload());
const auto &ref = resolve_preprocess(p, val, select_overload());

(builder.*(info.init))(ref.size());
auto list_builder{(builder.*(info.get_builder))()};

size_t i = 0;
for (const auto & cur : val) {
for (const auto & cur : ref) {
list_builder.set(i, cur);
++i;
}
Expand All @@ -342,22 +423,29 @@ inline auto capn_get(typename C::Reader &reader,
{
auto list_reader{(reader.*(info.get))()};
val.resize(list_reader.size());

size_t i = 0;
for (auto cur : list_reader) {
val[i] = cur;
++i;
}

capn_postprocess(val, select_overload());
}

template<typename T, typename R, typename B, typename C, size_t N>
inline auto capn_set(typename C::Builder &builder,
const CapnList<R, B, C> &info, const std::array<T, N> &val) ->
enable_if_<supports_capn_set<T>::value && !std::is_arithmetic<T>::value>
{
(builder.*(info.init))(val.size());
auto p = capn_preprocess(val, select_overload());
const auto &ref = resolve_preprocess(p, val, select_overload());

(builder.*(info.init))(ref.size());
auto list_builder{(builder.*(info.get_builder))()};

size_t i = 0;
for (const auto & cur : val) {
for (const auto & cur : ref) {
auto elem_builder = list_builder[i];
capn_set(elem_builder, cur);
++i;
Expand All @@ -370,6 +458,7 @@ inline auto capn_get(typename C::Reader &reader,
enable_if_<supports_capn_get<T>::value && !std::is_arithmetic<T>::value>
{
auto list_reader{(reader.*(info.get))()};

size_t i = 0;
for (auto cur : list_reader) {
capn_get(cur, val[i]);
Expand All @@ -378,20 +467,27 @@ inline auto capn_get(typename C::Reader &reader,
break;
}
}

for (; i < N; ++i) {
val[i] = T();
}

capn_postprocess(val, select_overload());
}

template<typename T, typename R, typename B, typename C, size_t N>
inline auto capn_set(typename C::Builder &builder,
const CapnList<R, B, C> &info, const std::array<T, N> &val) ->
enable_if_<std::is_arithmetic<T>::value>
{
(builder.*(info.init))(val.size());
auto p = capn_preprocess(val, select_overload());
const auto &ref = resolve_preprocess(p, val, select_overload());

(builder.*(info.init))(ref.size());
auto list_builder{(builder.*(info.get_builder))()};

size_t i = 0;
for (const auto & cur : val) {
for (const auto & cur : ref) {
list_builder.set(i, cur);
++i;
}
Expand All @@ -403,6 +499,7 @@ inline auto capn_get(typename C::Reader &reader,
enable_if_<std::is_arithmetic<T>::value>
{
auto list_reader{(reader.*(info.get))()};

size_t i = 0;
for (auto cur : list_reader) {
val[i] = cur;
Expand All @@ -411,9 +508,12 @@ inline auto capn_get(typename C::Reader &reader,
break;
}
}

for (; i < N; ++i) {
val[i] = T();
}

capn_postprocess(val, select_overload());
}

} // namespace knowledge
Expand Down
23 changes: 21 additions & 2 deletions tests/test_any.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,19 @@ namespace geo
(i16, I16)
)

std::unique_ptr<StampedPoseList> capn_preprocess(
const StampedPoseList &list, overload_priority<8>)
{
auto ret = list;
ret.i += 10;
return into_unique(std::move(ret));
}

void capn_postprocess(StampedPoseList &list, overload_priority<8>)
{
list.i -= 5;
}

static_assert(is_same<
decltype(get_capnproto_type_info_StampedPoseList_Strs(
type<geo_capn::StampedPoseList>{}, select_overload())),
Expand Down Expand Up @@ -604,6 +617,7 @@ void test_geo()
TEST_EQ(kb.get("vs0").share_any()->ref("list").size(), 3UL);
TEST_EQ(kb.get("vs0").get_any_cref()("list")[1]("stamp")("frame").to_string(), "frame1");
TEST_EQ(kb.get("vs0").get_any_cref()("arr3")[1].to_integer(), 6);
TEST_EQ(kb.get("vs0").get_any_cref()("i").to_integer(), 42);
TEST_EQ(kb.get("vs0").get_any_cref()("strs")[1].to_string(), "bar");
TEST_EQ(kb.get("vs0").get_any_cref()("en").to_integer(), 2);
TEST_EQ(kb.get("vs0").get_any_cref()("i16").to_integer(), (int)TestEnum::b);
Expand All @@ -618,17 +632,22 @@ void test_geo()
auto &avs0ref = avs0.ref<RegCapnObject>();
auto avs0_reader = avs0ref.reader();
auto avs0i = avs0_reader.get("i");
TEST_EQ(avs0i.template as<int>(), 42);
TEST_EQ(avs0i.template as<int>(), 52);

auto avs0_sreader = avs0.template reader<geo_capn::StampedPoseList>();
TEST_EQ(avs0_sreader.getI(), 42);
TEST_EQ(avs0_sreader.getI(), 52);

auto avs0data = avs0_reader.get("data");
auto avs0data_reader = avs0data.template as<capnp::List<int>>();
VAL(avs0data_reader[0]);

VAL(avs0);

Any::register_type<StampedPoseList>("StampedPoseList");
Any l2;
l2.tagged_unserialize(buf.data(), buf.size());
TEST_EQ(l2("i").to_integer(), 47);

Any new_pose = Any::construct("StampedPose");
VAL(new_pose);

Expand Down

0 comments on commit db28f9d

Please sign in to comment.