Skip to content

Commit

Permalink
[struct_pack][fix]add ID collsion compile-time check & doc. (#532)
Browse files Browse the repository at this point in the history
  • Loading branch information
poor-circle authored Dec 19, 2023
1 parent 394effa commit 3850f60
Show file tree
Hide file tree
Showing 13 changed files with 401 additions and 22 deletions.
18 changes: 15 additions & 3 deletions include/ylt/struct_pack.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ template <typename BaseClass, typename... DerivedClasses, typename Reader,
if constexpr (has_hash_collision != 0) {
static_assert(!sizeof(std::tuple_element_t<has_hash_collision,
std::tuple<DerivedClasses...>>),
"hash collision happened, consider add member `static "
"ID collision happened, consider add member `static "
"constexpr uint64_t struct_pack_id` for collision type. ");
}
else {
Expand All @@ -687,14 +687,26 @@ template <
struct_pack::expected<std::unique_ptr<BaseClass>, struct_pack::errc>
deserialize_derived_class(const View &v) {
detail::memory_reader reader{v.data(), v.data() + v.size()};
return deserialize_derived_class<BaseClass, DerivedClasses...>(reader);
if constexpr (std::is_abstract_v<BaseClass>) {
return deserialize_derived_class<BaseClass, DerivedClasses...>(reader);
}
else {
return deserialize_derived_class<BaseClass, BaseClass, DerivedClasses...>(
reader);
}
}
template <typename BaseClass, typename... DerivedClasses>
[[nodiscard]] STRUCT_PACK_INLINE
struct_pack::expected<std::unique_ptr<BaseClass>, struct_pack::errc>
deserialize_derived_class(const char *data, size_t size) {
detail::memory_reader reader{data, data + size};
return deserialize_derived_class<BaseClass, DerivedClasses...>(reader);
if constexpr (std::is_abstract_v<BaseClass>) {
return deserialize_derived_class<BaseClass, DerivedClasses...>(reader);
}
else {
return deserialize_derived_class<BaseClass, BaseClass, DerivedClasses...>(
reader);
}
}

} // namespace struct_pack
25 changes: 25 additions & 0 deletions include/ylt/struct_pack/derived_helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,5 +202,30 @@ constexpr auto derived_decl_impl() {
return struct_pack::detail::declval<std::tuple<Base, Derives...>>();
}
}

template <typename Base, typename... Derives>
constexpr bool check_ID_collision() {
if constexpr (std::is_abstract_v<Base>) {
constexpr auto has_collision = struct_pack::detail::MD5_set<
std::tuple<Derives...>>::has_hash_collision;
if constexpr (has_collision != 0) {
static_assert(
!sizeof(std::tuple_element_t<has_collision, std::tuple<Derives...>>),
"ID collision happened, consider add member `static "
"constexpr uint64_t struct_pack_id` for collision type. ");
}
}
else {
constexpr auto has_collision = struct_pack::detail::MD5_set<
std::tuple<Base, Derives...>>::has_hash_collision;
if constexpr (has_collision != 0) {
static_assert(!sizeof(std::tuple_element_t<has_collision,
std::tuple<Base, Derives...>>),
"ID collision happened, consider add member `static "
"constexpr uint64_t struct_pack_id` for collision type. ");
}
}
return true;
}
} // namespace detail
} // namespace struct_pack
11 changes: 4 additions & 7 deletions include/ylt/struct_pack/derived_marco.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,19 @@
#include "foreach_macro.h"

#pragma once
#define GET_STRUCT_PACK_ID_IMPL(type) \
inline uint32_t type::get_struct_pack_id() const { \
return struct_pack::detail::get_struct_pack_id_impl<type>(); \
}

#define GET_STRUCT_PACK_ID_IMPL_FOR_LOOP(idx, type) \
inline uint32_t type::get_struct_pack_id() const { \
return struct_pack::detail::get_struct_pack_id_impl<type>(); \
}

#define STRUCT_PACK_DERIVED_IMPL(base, ...) \
#define STRUCT_PACK_DERIVED_DECL(base, ...) \
\
inline decltype(struct_pack::detail::derived_decl_impl<base, __VA_ARGS__>()) \
struct_pack_derived_decl(base*);

#define STRUCT_PACK_DERIVED_DECL(base, ...) \
#define STRUCT_PACK_DERIVED_IMPL(base, ...) \
STRUCT_PACK_EXPAND_EACH(, GET_STRUCT_PACK_ID_IMPL_FOR_LOOP, base, \
__VA_ARGS__) \
STRUCT_PACK_DERIVED_IMPL(base, __VA_ARGS__)
STRUCT_PACK_DERIVED_DECL(base, __VA_ARGS__); \
static_assert(struct_pack::detail::check_ID_collision<base, __VA_ARGS__>());\
2 changes: 1 addition & 1 deletion src/struct_pack/examples/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")

cc_binary(
name = "serialize_example",
srcs = ["basic_usage.cpp","main.cpp","non_aggregated_type.cpp","serialize_config.cpp","user_defined_serialization.cpp"],
srcs = ["basic_usage.cpp","main.cpp","non_aggregated_type.cpp","serialize_config.cpp","user_defined_serialization.cpp","derived_class.cpp"],
copts = ["-std=c++20"],
deps = [
"//:ylt"
Expand Down
2 changes: 1 addition & 1 deletion src/struct_pack/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ else()
# include_directories(include)
# include_directories(include/ylt/thirdparty)
endif()
add_executable(struct_pack_example basic_usage.cpp non_aggregated_type.cpp serialize_config.cpp user_defined_serialization.cpp main.cpp)
add_executable(struct_pack_example basic_usage.cpp non_aggregated_type.cpp serialize_config.cpp user_defined_serialization.cpp derived_class.cpp main.cpp)
146 changes: 146 additions & 0 deletions src/struct_pack/examples/derived_class.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Copyright (c) 2023, Alibaba Group Holding Limited;
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "ylt/struct_pack.hpp"
namespace virtual_base_class {

// base
// / |
// obj1 obj2
// |
// obj3

struct base {
uint64_t ID;
virtual uint32_t get_struct_pack_id()
const = 0; // user must declare this virtual function in derived
// class
virtual ~base(){};
virtual std::string_view hello() = 0;
};
struct obj1 : public base {
std::string name;
virtual uint32_t get_struct_pack_id()
const override; // user must declare this virtual function in derived
// class
std::string_view hello() override { return "obj1"; }
};
STRUCT_PACK_REFL(obj1, ID, name);
struct obj2 : public base {
std::array<float, 5> data;
virtual uint32_t get_struct_pack_id() const override;
std::string_view hello() override { return "obj2"; }
};
STRUCT_PACK_REFL(obj2, ID, data);
struct obj3 : public obj1 {
int age;
virtual uint32_t get_struct_pack_id() const override;
std::string_view hello() override { return "obj3"; }
};
STRUCT_PACK_REFL(obj3, ID, name, age);
// the declartion for derived relation for struct_pack
STRUCT_PACK_DERIVED_DECL(base, obj1, obj2, obj3);
// the implement for derived relation for struct_pack
STRUCT_PACK_DERIVED_IMPL(base, obj1, obj2, obj3);
} // namespace virtual_base_class
void virtual_base_class_example(void) {
using namespace virtual_base_class;
std::vector<std::unique_ptr<base>> data;
data.emplace_back(std::make_unique<obj2>());
data.emplace_back(std::make_unique<obj1>());
data.emplace_back(std::make_unique<obj3>());
auto checker = std::vector{"obj2", "obj1", "obj3"};
auto ret = struct_pack::serialize(data);
auto result =
struct_pack::deserialize<std::vector<std::unique_ptr<base>>>(ret);
assert(result.has_value()); // check deserialize ok
assert(result->size() == 3); // check vector size
for (int i = 0; i < result->size(); ++i) {
assert(checker[i] == result.value()[i]->hello()); // check type
}
}

namespace base_class {

// base
// / |
// obj1 obj2
// |
// obj3

struct base {
uint64_t ID;
virtual uint32_t get_struct_pack_id() const;
virtual ~base(){};
virtual std::string_view hello() { return "base"; }
};
STRUCT_PACK_REFL(base, ID);
struct obj1 : public base {
std::string name;
virtual uint32_t get_struct_pack_id() const override;
std::string_view hello() override { return "obj1"; }
};
STRUCT_PACK_REFL(obj1, ID, name);
struct obj2 : public base {
std::array<float, 5> data;
virtual uint32_t get_struct_pack_id() const override;
std::string_view hello() override { return "obj2"; }
};
STRUCT_PACK_REFL(obj2, ID, data);
struct obj3 : public obj1 {
virtual uint32_t get_struct_pack_id() const override;
static constexpr uint64_t struct_pack_id = 114514;
std::string_view hello() override { return "obj3"; }
};
STRUCT_PACK_REFL(obj3, ID, name);
STRUCT_PACK_DERIVED_DECL(base, obj1, obj2, obj3);
STRUCT_PACK_DERIVED_IMPL(base, obj1, obj2, obj3);
} // namespace base_class

void base_class_example(void) {
using namespace base_class;
std::vector<std::unique_ptr<base>> data;
data.emplace_back(std::make_unique<obj2>());
data.emplace_back(std::make_unique<obj1>());
data.emplace_back(std::make_unique<obj3>());
data.emplace_back(std::make_unique<base>());
auto ret = struct_pack::serialize(data);
auto checker = std::vector{"obj2", "obj1", "obj3", "base"};
auto result =
struct_pack::deserialize<std::vector<std::unique_ptr<base>>>(ret);
assert(result.has_value());
assert(result->size() == 4); // check vector size
for (int i = 0; i < result->size(); ++i) {
assert(checker[i] == result.value()[i]->hello()); // check type
}
}

void deserialize_derived_class_example() {
using namespace base_class;
auto buffer = struct_pack::serialize(obj3{});
auto result =
struct_pack::deserialize_derived_class<base, obj1, obj2, obj3>(buffer);
assert(result.has_value());
std::unique_ptr<base> ptr = std::move(result.value());
assert(ptr != nullptr);
assert(ptr->hello() == "obj3");
}

void derived_class_example() {
virtual_base_class_example();
base_class_example();
deserialize_derived_class_example();
}
2 changes: 2 additions & 0 deletions src/struct_pack/examples/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ void basic_usage();
void non_aggregated_type();
void serialize_config();
void user_defined_serialization();
void derived_class_example();
int main() {
basic_usage();
non_aggregated_type();
serialize_config();
user_defined_serialization();
derived_class_example();
return 0;
}
2 changes: 0 additions & 2 deletions src/struct_pack/examples/serialize_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

#include <cassert>

#include "ylt/struct_pack/reflection.hpp"

#define STRUCT_PACK_OPTIMIZE // add this macro to speed up
// serialize/deserialize but it will cost more
// time to compile
Expand Down
16 changes: 16 additions & 0 deletions src/struct_pack/examples/user_defined_serialization.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* Copyright (c) 2023, Alibaba Group Holding Limited;
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <cstdlib>

#include "ylt/struct_pack.hpp"
Expand Down
4 changes: 2 additions & 2 deletions src/struct_pack/tests/test_derived.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ struct foo2 : public Base {
friend bool operator==(const foo2& a, const foo2& b) {
return a.id == b.id && a.hello == b.hello && a.hi == b.hi;
}
static constexpr uint64_t struct_pack_id = 114514;
static constexpr int struct_pack_id = 114514;
};
STRUCT_PACK_REFL(foo2, id, hello, hi);
struct foo3 : public Base {
Expand Down Expand Up @@ -69,7 +69,7 @@ STRUCT_PACK_REFL(gua, id, a, b);

struct_pack::expected<std::unique_ptr<Base>, struct_pack::errc>
Base::deserialize(std::string_view sv) {
return struct_pack::deserialize_derived_class<Base, Base, bar, foo, gua, foo2,
return struct_pack::deserialize_derived_class<Base, bar, foo, gua, foo2,
foo4>(sv);
}
} // namespace test1
Expand Down
4 changes: 2 additions & 2 deletions src/struct_pack/tests/test_derived.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ struct derived4 : public derived3 {

STRUCT_PACK_REFL(derived4, a, d, e);

STRUCT_PACK_DERIVED_DECL(base, derived1, derived2, derived3, derived4);
STRUCT_PACK_DERIVED_IMPL(base, derived1, derived2, derived3, derived4);
} // namespace test2
namespace test3 {

Expand Down Expand Up @@ -93,6 +93,6 @@ struct derived4 : public derived3 {

STRUCT_PACK_REFL(derived4, a, d, e);

STRUCT_PACK_DERIVED_DECL(base, derived1, derived2, derived3, derived4);
STRUCT_PACK_DERIVED_IMPL(base, derived1, derived2, derived3, derived4);

} // namespace test3
Loading

0 comments on commit 3850f60

Please sign in to comment.