Skip to content

Commit

Permalink
[struct_pack][feat] add derived class into struct_pack type system (#463
Browse files Browse the repository at this point in the history
)
  • Loading branch information
poor-circle authored Sep 21, 2023
1 parent aca25a3 commit daedb6a
Show file tree
Hide file tree
Showing 12 changed files with 1,172 additions and 619 deletions.
12 changes: 9 additions & 3 deletions include/ylt/struct_pack.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <type_traits>
#include <utility>

#include "struct_pack/derived_marco.hpp"
#include "struct_pack/struct_pack_impl.hpp"

#if __has_include(<expected>) && __cplusplus > 202002L
Expand Down Expand Up @@ -91,7 +92,12 @@ STRUCT_PACK_INLINE constexpr std::uint32_t get_type_code() {
static_assert(sizeof...(Args) > 0);
std::uint32_t ret = 0;
if constexpr (sizeof...(Args) == 1) {
ret = detail::get_types_code<Args...>();
if constexpr (std::is_abstract_v<Args...>) {
struct_pack::detail::unreachable();
}
else {
ret = detail::get_types_code<Args...>();
}
}
else {
ret = detail::get_types_code<std::tuple<detail::remove_cvref_t<Args>...>>();
Expand Down Expand Up @@ -527,8 +533,8 @@ template <typename BaseClass, typename... DerivedClasses, typename Reader,
struct_pack::detail::public_base_class_checker<
BaseClass, std::tuple<DerivedClasses...>>::value,
"the First type should be the base class of all derived class ");
constexpr auto has_hash_collision =
struct_pack::detail::MD5_set<DerivedClasses...>::has_hash_collision;
constexpr auto has_hash_collision = struct_pack::detail::MD5_set<
std::tuple<DerivedClasses...>>::has_hash_collision;
if constexpr (has_hash_collision != 0) {
static_assert(!sizeof(std::tuple_element_t<has_hash_collision,
std::tuple<DerivedClasses...>>),
Expand Down
222 changes: 222 additions & 0 deletions include/ylt/struct_pack/derived_helper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/*
* 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.
*/
#pragma once
#include <cassert>
#include <cstdint>
#include <memory>
#include <tuple>

#include "error_code.hpp"
#include "marco.h"
#include "size_info.hpp"
#include "util.h"
namespace struct_pack {

template <typename... Args>
STRUCT_PACK_INLINE constexpr std::uint32_t get_type_code();
namespace detail {
struct MD5_pair {
uint32_t md5;
uint32_t index;
constexpr friend bool operator<(const MD5_pair &l, const MD5_pair &r) {
return l.md5 < r.md5;
}
constexpr friend bool operator>(const MD5_pair &l, const MD5_pair &r) {
return l.md5 > r.md5;
}
constexpr friend bool operator==(const MD5_pair &l, const MD5_pair &r) {
return l.md5 == r.md5;
}
};

template <typename T>
constexpr uint32_t get_types_code();

template <typename DerivedClasses>
struct MD5_set {
static constexpr int size = std::tuple_size_v<DerivedClasses>;
static_assert(size <= 256);

private:
template <std::size_t... Index>
static constexpr std::array<MD5_pair, size> calculate_md5(
std::index_sequence<Index...>) {
std::array<MD5_pair, size> md5{};
((md5[Index] =
MD5_pair{
get_types_code<std::tuple_element_t<Index, DerivedClasses>>(),
Index}),
...);
compile_time_sort(md5);
return md5;
}
static constexpr std::size_t has_hash_collision_impl() {
for (std::size_t i = 1; i < size; ++i) {
if (value[i - 1] == value[i]) {
return value[i].index;
}
}
return 0;
}

public:
static constexpr std::array<MD5_pair, size> value =
calculate_md5(std::make_index_sequence<size>());
static constexpr std::size_t has_hash_collision = has_hash_collision_impl();
};

template <typename BaseClass, typename DerivedClasses>
struct public_base_class_checker {
static_assert(std::tuple_size_v<DerivedClasses> <= 256);

private:
template <std::size_t... Index>
static constexpr bool calculate_md5(std::index_sequence<Index...>) {
return (std::is_base_of_v<BaseClass,
std::tuple_element_t<Index, DerivedClasses>> &&
...);
}

public:
static constexpr bool value = public_base_class_checker::calculate_md5(
std::make_index_sequence<std::tuple_size_v<DerivedClasses>>());
};

template <typename DerivedClasses>
struct deserialize_derived_class_helper {
template <size_t index, typename BaseClass, typename unpack>
static STRUCT_PACK_INLINE constexpr struct_pack::errc run(
std::unique_ptr<BaseClass> &base, unpack &unpacker) {
if constexpr (index >= std::tuple_size_v<DerivedClasses>) {
unreachable();
return struct_pack::errc{};
}
else {
using derived_class = std::tuple_element_t<index, DerivedClasses>;
base = std::make_unique<derived_class>();
return unpacker.deserialize(*(derived_class *)base.get());
}
}
};

template <typename T>
constexpr size_info inline calculate_one_size(const T &item);

template <typename DerivedClasses>
struct calculate_one_size_derived_class_helper {
template <size_t index, typename BaseClass>
static STRUCT_PACK_INLINE constexpr size_info run(BaseClass *base) {
if constexpr (index >= std::tuple_size_v<DerivedClasses>) {
unreachable();
}
else {
using derived_class = std::tuple_element_t<index, DerivedClasses>;
#ifdef STRUCT_PACK_RTTI_ENABLED
assert(dynamic_cast<derived_class *>(base));
#endif
return calculate_one_size(*(derived_class *)base);
}
}
};

template <typename DerivedClasses, typename size_type, typename version>
struct serialize_one_derived_class_helper {
template <size_t index, typename packer, typename BaseClass>
static STRUCT_PACK_INLINE constexpr void run(packer *self, BaseClass *base) {
if constexpr (index >= std::tuple_size_v<DerivedClasses>) {
unreachable();
}
else {
using derived_class = std::tuple_element_t<index, DerivedClasses>;
#ifdef STRUCT_PACK_RTTI_ENABLED
assert(dynamic_cast<derived_class *>(base));
#endif
self->template serialize_one<size_type::value, version::value>(
*(derived_class *)base);
}
}
};

template <typename DerivedClasses, typename size_type, typename version,
typename NotSkip>
struct deserialize_one_derived_class_helper {
template <size_t index, typename unpacker, typename Pointer>
static STRUCT_PACK_INLINE constexpr struct_pack::errc run(unpacker *self,
Pointer &base) {
if constexpr (index >= std::tuple_size_v<DerivedClasses>) {
unreachable();
}
else {
using derived_class = std::tuple_element_t<index, DerivedClasses>;
base = std::make_unique<derived_class>();
return self->template deserialize_one<size_type::value, version::value,
NotSkip::value>(
*(derived_class *)base.get());
}
}
};

template <typename DerivedClasses, typename size_type, typename version>
struct deserialize_one_compatible_in_derived_class_helper {
template <size_t index, typename unpacker, typename Pointer>
static STRUCT_PACK_INLINE constexpr struct_pack::errc run(unpacker *self,
Pointer &base) {
if constexpr (index >= std::tuple_size_v<DerivedClasses>) {
unreachable();
}
else {
using derived_class = std::tuple_element_t<index, DerivedClasses>;
#ifdef STRUCT_PACK_RTTI_ENABLED
assert(dynamic_cast<derived_class *>(base.get()));
#endif
return self->template deserialize_one<size_type::value, version::value>(
*(derived_class *)base.get());
}
}
};

template <typename Base>
using derived_class_set_t = decltype(struct_pack_derived_decl((Base *)nullptr));

template <typename Base>
constexpr std::size_t search_type_by_md5(uint32_t id, bool &ok) {
constexpr auto &MD5s = MD5_set<derived_class_set_t<Base>>::value;
auto result = std::lower_bound(MD5s.begin(), MD5s.end(), MD5_pair{id, 0});
ok = (result != MD5s.end() && result->md5 == id);
return result->index;
}

template <typename T>
std::uint32_t get_struct_pack_id_impl() {
if constexpr (std::is_abstract_v<T>) {
struct_pack::detail::unreachable();
}
else {
return struct_pack::get_type_code<T>();
}
}

template <typename Base, typename... Derives>
constexpr auto derived_decl_impl() {
if constexpr (std::is_abstract_v<Base>) {
return struct_pack::detail::declval<std::tuple<Derives...>>();
}
else {
return struct_pack::detail::declval<std::tuple<Base, Derives...>>();
}
}
} // namespace detail
} // namespace struct_pack
38 changes: 38 additions & 0 deletions include/ylt/struct_pack/derived_marco.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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 "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, ...) \
\
inline decltype(struct_pack::detail::derived_decl_impl<base, __VA_ARGS__>()) \
struct_pack_derived_decl(base*);

#define STRUCT_PACK_DERIVED_DECL(base, ...) \
STRUCT_PACK_EXPAND_EACH(, GET_STRUCT_PACK_ID_IMPL_FOR_LOOP, base, \
__VA_ARGS__) \
STRUCT_PACK_DERIVED_IMPL(base, __VA_ARGS__)\
Loading

0 comments on commit daedb6a

Please sign in to comment.