Skip to content

Commit

Permalink
[struct_pack] support non-aggregate type (#382)
Browse files Browse the repository at this point in the history
* [struct_pack] add support for non-aggregated type
  • Loading branch information
poor-circle authored Jul 25, 2023
1 parent 2227e38 commit c90d1ad
Show file tree
Hide file tree
Showing 15 changed files with 1,059 additions and 19 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ include(cmake/install.cmake)
include(cmake/config.cmake)
# add project's source such as unit test, example & benchmark
include(cmake/subdir.cmake)

include(cmake/platform.cmake)

15 changes: 15 additions & 0 deletions cmake/platform.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# --------------------- Gcc
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines")
#-ftree-slp-vectorize with coroutine cause link error. disable it util gcc fix.
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-tree-slp-vectorize")
endif()
# --------------------- Clang

# --------------------- Msvc
# Resolves C1128 complained by MSVC: number of sections exceeded object file format limit: compile with /bigobj.
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/bigobj>")
# Resolves C4737 complained by MSVC: C4737: Unable to perform required tail call. Performance may be degraded. "Release-Type only"
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/EHa>")
endif()
377 changes: 374 additions & 3 deletions include/ylt/struct_pack/reflection.hpp

Large diffs are not rendered by default.

25 changes: 20 additions & 5 deletions include/ylt/struct_pack/struct_pack_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1229,8 +1229,12 @@ constexpr size_info inline calculate_one_size(const T &item) {
}
}
else if constexpr (std::is_class_v<type>) {
if constexpr (!pair<type> && !is_trivial_tuple<type>)
static_assert(std::is_aggregate_v<std::remove_cvref_t<type>>);
if constexpr (!pair<type> && !is_trivial_tuple<type>) {
if constexpr (!user_defined_refl<type>)
static_assert(std::is_aggregate_v<std::remove_cvref_t<type>>,
"struct_pack only support aggregated type, or you should "
"add macro STRUCT_PACK_REFL(Type,field1,field2...)");
}
if constexpr (is_trivial_serializable<type>::value) {
ret.total = sizeof(type);
}
Expand Down Expand Up @@ -1803,7 +1807,11 @@ class packer {
}
else if constexpr (std::is_class_v<type>) {
if constexpr (!pair<type> && !is_trivial_tuple<type>)
static_assert(std::is_aggregate_v<std::remove_cvref_t<type>>);
if constexpr (!user_defined_refl<type>)
static_assert(
std::is_aggregate_v<std::remove_cvref_t<type>>,
"struct_pack only support aggregated type, or you should "
"add macro STRUCT_PACK_REFL(Type,field1,field2...)");
if constexpr (is_trivial_serializable<type>::value) {
writer_.write((char *)&item, sizeof(type));
}
Expand Down Expand Up @@ -2300,7 +2308,10 @@ class unpacker {
t);
}
else if constexpr (std::is_class_v<T>) {
static_assert(std::is_aggregate_v<T>);
if constexpr (!user_defined_refl<T>)
static_assert(std::is_aggregate_v<std::remove_cvref_t<T>>,
"struct_pack only support aggregated type, or you should "
"add macro STRUCT_PACK_REFL(Type,field1,field2...)");
err_code = visit_members(t, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {
static_assert(I < sizeof...(items), "out of range");
return for_each<size_type, version, I>(field, items...);
Expand Down Expand Up @@ -2659,7 +2670,11 @@ class unpacker {
}
else if constexpr (std::is_class_v<type>) {
if constexpr (!pair<type> && !is_trivial_tuple<type>)
static_assert(std::is_aggregate_v<std::remove_cvref_t<type>>);
if constexpr (!user_defined_refl<type>)
static_assert(
std::is_aggregate_v<std::remove_cvref_t<type>>,
"struct_pack only support aggregated type, or you should "
"add macro STRUCT_PACK_REFL(Type,field1,field2...)");
if constexpr (is_trivial_serializable<type>::value) {
if constexpr (NotSkip) {
if (!reader_.read((char *)&item, sizeof(type))) [[unlikely]] {
Expand Down
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 = ["serialize.cpp"],
srcs = ["basic_usage.cpp","main.cpp","non_aggregated_type.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 serialize.cpp)
add_executable(struct_pack_example basic_usage.cpp non_aggregated_type.cpp main.cpp)
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <fcntl.h>

#include <cassert>
#include <cstdint>
Expand Down Expand Up @@ -147,6 +146,4 @@ void basic_usage() {
auto p4 = struct_pack::deserialize<person>(ifs);
assert(p4 == p);
}
}

int main() { basic_usage(); }
}
24 changes: 24 additions & 0 deletions src/struct_pack/examples/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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.
*/

void basic_usage();
void non_aggregated_type();

int main() {
basic_usage();
non_aggregated_type();
return 0;
}
151 changes: 151 additions & 0 deletions src/struct_pack/examples/non_aggregated_type.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* 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 <cassert>
#include <cstdint>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <memory>
#include <ostream>
#include <sstream>

// It's easy to use struct_pack, just include it!
#include <ylt/struct_pack.hpp>

// 1. make sure your type has a default constructor
// 2. add marco STRUCT_PACK_REFL(Type, field1, field2...) in the same namespace
namespace example {
class person : std::vector<int> {
private:
std::string mess;

public:
int age;
std::string name;
auto operator==(const person& rhs) const {
return age == rhs.age && name == rhs.name;
}
person() = default;
person(int age, const std::string& name) : age(age), name(name) {}
};

STRUCT_PACK_REFL(person, age, name);
} // namespace example

// 3. if you want to use private field, add friend declartion marco

namespace example2 {
class person {
private:
int age;
std::string name;

public:
auto operator==(const person& rhs) const {
return age == rhs.age && name == rhs.name;
}
person() = default;
person(int age, const std::string& name) : age(age), name(name) {}
STRUCT_PACK_FRIEND_DECL(person);
};
STRUCT_PACK_REFL(person, age, name);
} // namespace example2

// 4. you can also add function which return class member reference as
// struct_pack field.

namespace example3 {
class person {
private:
int age_;
std::string name_;

public:
auto operator==(const person& rhs) const {
return age_ == rhs.age_ && name_ == rhs.name_;
}
person() = default;
person(int age, const std::string& name) : age_(age), name_(name) {}

int& age() { return age_; };
const int& age() const { return age_; };
std::string& name() { return name_; };
const std::string& name() const { return name_; };
};
STRUCT_PACK_REFL(person, age(), name());
} // namespace example3

// 5. Remember, the STURCT_PACK_REFL marco disable the trivial_serialize
// optimize. So don't use it for trivial type.
namespace example4 {
struct point {
int x, y, z;
};
STRUCT_PACK_REFL(point, x, y, z);
struct point2 {
int x, y, z;
};
} // namespace example4

// 6. example5::person ,example::person, example2::person, example3:person are
// same type in struct_pack type system.
namespace example5 {
struct person {
int age;
std::string name;
auto operator==(const person& rhs) const {
return age == rhs.age && name == rhs.name;
}
};
} // namespace example5

//clang-format off
void non_aggregated_type() {
{
example::person p{20, "tom"};
auto buffer = struct_pack::serialize(p);
auto p2 = struct_pack::deserialize<example::person>(buffer);
assert(p2);
assert(p == p2.value());
}
{
example2::person p{20, "tom"};
auto buffer = struct_pack::serialize(p);
auto p2 = struct_pack::deserialize<example2::person>(buffer);
assert(p2);
assert(p == p2.value());
}
{
example2::person p{20, "tom"};
auto buffer = struct_pack::serialize(p);
auto p3 = struct_pack::deserialize<example2::person>(buffer);
assert(p3);
assert(p == p3.value());
}
{
assert(struct_pack::get_type_code<example4::point>() !=
struct_pack::get_type_code<example4::point2>());
}
{
assert(struct_pack::get_type_code<example5::person>() ==
struct_pack::get_type_code<example::person>());
assert(struct_pack::get_type_code<example5::person>() ==
struct_pack::get_type_code<example2::person>());
assert(struct_pack::get_type_code<example5::person>() ==
struct_pack::get_type_code<example3::person>());
}
}
1 change: 1 addition & 0 deletions src/struct_pack/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ add_executable(struct_pack_test
test_varint.cpp
test_stream.cpp
test_compatible.cpp
test_non_aggregated_type.cpp
main.cpp
)
add_test(NAME struct_pack_test COMMAND struct_pack_test)
Expand Down
Loading

0 comments on commit c90d1ad

Please sign in to comment.