Skip to content

Commit c90d1ad

Browse files
authored
[struct_pack] support non-aggregate type (#382)
* [struct_pack] add support for non-aggregated type
1 parent 2227e38 commit c90d1ad

15 files changed

+1059
-19
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,5 @@ include(cmake/install.cmake)
2626
include(cmake/config.cmake)
2727
# add project's source such as unit test, example & benchmark
2828
include(cmake/subdir.cmake)
29-
29+
include(cmake/platform.cmake)
3030

cmake/platform.cmake

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# --------------------- Gcc
2+
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
3+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines")
4+
#-ftree-slp-vectorize with coroutine cause link error. disable it util gcc fix.
5+
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-tree-slp-vectorize")
6+
endif()
7+
# --------------------- Clang
8+
9+
# --------------------- Msvc
10+
# Resolves C1128 complained by MSVC: number of sections exceeded object file format limit: compile with /bigobj.
11+
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/bigobj>")
12+
# Resolves C4737 complained by MSVC: C4737: Unable to perform required tail call. Performance may be degraded. "Release-Type only"
13+
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
14+
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/EHa>")
15+
endif()

include/ylt/struct_pack/reflection.hpp

+374-3
Large diffs are not rendered by default.

include/ylt/struct_pack/struct_pack_impl.hpp

+20-5
Original file line numberDiff line numberDiff line change
@@ -1229,8 +1229,12 @@ constexpr size_info inline calculate_one_size(const T &item) {
12291229
}
12301230
}
12311231
else if constexpr (std::is_class_v<type>) {
1232-
if constexpr (!pair<type> && !is_trivial_tuple<type>)
1233-
static_assert(std::is_aggregate_v<std::remove_cvref_t<type>>);
1232+
if constexpr (!pair<type> && !is_trivial_tuple<type>) {
1233+
if constexpr (!user_defined_refl<type>)
1234+
static_assert(std::is_aggregate_v<std::remove_cvref_t<type>>,
1235+
"struct_pack only support aggregated type, or you should "
1236+
"add macro STRUCT_PACK_REFL(Type,field1,field2...)");
1237+
}
12341238
if constexpr (is_trivial_serializable<type>::value) {
12351239
ret.total = sizeof(type);
12361240
}
@@ -1803,7 +1807,11 @@ class packer {
18031807
}
18041808
else if constexpr (std::is_class_v<type>) {
18051809
if constexpr (!pair<type> && !is_trivial_tuple<type>)
1806-
static_assert(std::is_aggregate_v<std::remove_cvref_t<type>>);
1810+
if constexpr (!user_defined_refl<type>)
1811+
static_assert(
1812+
std::is_aggregate_v<std::remove_cvref_t<type>>,
1813+
"struct_pack only support aggregated type, or you should "
1814+
"add macro STRUCT_PACK_REFL(Type,field1,field2...)");
18071815
if constexpr (is_trivial_serializable<type>::value) {
18081816
writer_.write((char *)&item, sizeof(type));
18091817
}
@@ -2300,7 +2308,10 @@ class unpacker {
23002308
t);
23012309
}
23022310
else if constexpr (std::is_class_v<T>) {
2303-
static_assert(std::is_aggregate_v<T>);
2311+
if constexpr (!user_defined_refl<T>)
2312+
static_assert(std::is_aggregate_v<std::remove_cvref_t<T>>,
2313+
"struct_pack only support aggregated type, or you should "
2314+
"add macro STRUCT_PACK_REFL(Type,field1,field2...)");
23042315
err_code = visit_members(t, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {
23052316
static_assert(I < sizeof...(items), "out of range");
23062317
return for_each<size_type, version, I>(field, items...);
@@ -2659,7 +2670,11 @@ class unpacker {
26592670
}
26602671
else if constexpr (std::is_class_v<type>) {
26612672
if constexpr (!pair<type> && !is_trivial_tuple<type>)
2662-
static_assert(std::is_aggregate_v<std::remove_cvref_t<type>>);
2673+
if constexpr (!user_defined_refl<type>)
2674+
static_assert(
2675+
std::is_aggregate_v<std::remove_cvref_t<type>>,
2676+
"struct_pack only support aggregated type, or you should "
2677+
"add macro STRUCT_PACK_REFL(Type,field1,field2...)");
26632678
if constexpr (is_trivial_serializable<type>::value) {
26642679
if constexpr (NotSkip) {
26652680
if (!reader_.read((char *)&item, sizeof(type))) [[unlikely]] {

src/struct_pack/examples/BUILD.bazel

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
22

33
cc_binary(
44
name = "serialize_example",
5-
srcs = ["serialize.cpp"],
5+
srcs = ["basic_usage.cpp","main.cpp","non_aggregated_type.cpp"],
66
copts = ["-std=c++20"],
77
deps = [
88
"//:ylt"

src/struct_pack/examples/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ else()
1818
# include_directories(include)
1919
# include_directories(include/ylt/thirdparty)
2020
endif()
21-
add_executable(struct_pack_example serialize.cpp)
21+
add_executable(struct_pack_example basic_usage.cpp non_aggregated_type.cpp main.cpp)

src/struct_pack/examples/serialize.cpp src/struct_pack/examples/basic_usage.cpp

+1-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
#include <fcntl.h>
1716

1817
#include <cassert>
1918
#include <cstdint>
@@ -147,6 +146,4 @@ void basic_usage() {
147146
auto p4 = struct_pack::deserialize<person>(ifs);
148147
assert(p4 == p);
149148
}
150-
}
151-
152-
int main() { basic_usage(); }
149+
}

src/struct_pack/examples/main.cpp

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) 2023, Alibaba Group Holding Limited;
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
void basic_usage();
18+
void non_aggregated_type();
19+
20+
int main() {
21+
basic_usage();
22+
non_aggregated_type();
23+
return 0;
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Copyright (c) 2023, Alibaba Group Holding Limited;
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <cassert>
18+
#include <cstdint>
19+
#include <cstdio>
20+
#include <fstream>
21+
#include <iostream>
22+
#include <memory>
23+
#include <ostream>
24+
#include <sstream>
25+
26+
// It's easy to use struct_pack, just include it!
27+
#include <ylt/struct_pack.hpp>
28+
29+
// 1. make sure your type has a default constructor
30+
// 2. add marco STRUCT_PACK_REFL(Type, field1, field2...) in the same namespace
31+
namespace example {
32+
class person : std::vector<int> {
33+
private:
34+
std::string mess;
35+
36+
public:
37+
int age;
38+
std::string name;
39+
auto operator==(const person& rhs) const {
40+
return age == rhs.age && name == rhs.name;
41+
}
42+
person() = default;
43+
person(int age, const std::string& name) : age(age), name(name) {}
44+
};
45+
46+
STRUCT_PACK_REFL(person, age, name);
47+
} // namespace example
48+
49+
// 3. if you want to use private field, add friend declartion marco
50+
51+
namespace example2 {
52+
class person {
53+
private:
54+
int age;
55+
std::string name;
56+
57+
public:
58+
auto operator==(const person& rhs) const {
59+
return age == rhs.age && name == rhs.name;
60+
}
61+
person() = default;
62+
person(int age, const std::string& name) : age(age), name(name) {}
63+
STRUCT_PACK_FRIEND_DECL(person);
64+
};
65+
STRUCT_PACK_REFL(person, age, name);
66+
} // namespace example2
67+
68+
// 4. you can also add function which return class member reference as
69+
// struct_pack field.
70+
71+
namespace example3 {
72+
class person {
73+
private:
74+
int age_;
75+
std::string name_;
76+
77+
public:
78+
auto operator==(const person& rhs) const {
79+
return age_ == rhs.age_ && name_ == rhs.name_;
80+
}
81+
person() = default;
82+
person(int age, const std::string& name) : age_(age), name_(name) {}
83+
84+
int& age() { return age_; };
85+
const int& age() const { return age_; };
86+
std::string& name() { return name_; };
87+
const std::string& name() const { return name_; };
88+
};
89+
STRUCT_PACK_REFL(person, age(), name());
90+
} // namespace example3
91+
92+
// 5. Remember, the STURCT_PACK_REFL marco disable the trivial_serialize
93+
// optimize. So don't use it for trivial type.
94+
namespace example4 {
95+
struct point {
96+
int x, y, z;
97+
};
98+
STRUCT_PACK_REFL(point, x, y, z);
99+
struct point2 {
100+
int x, y, z;
101+
};
102+
} // namespace example4
103+
104+
// 6. example5::person ,example::person, example2::person, example3:person are
105+
// same type in struct_pack type system.
106+
namespace example5 {
107+
struct person {
108+
int age;
109+
std::string name;
110+
auto operator==(const person& rhs) const {
111+
return age == rhs.age && name == rhs.name;
112+
}
113+
};
114+
} // namespace example5
115+
116+
//clang-format off
117+
void non_aggregated_type() {
118+
{
119+
example::person p{20, "tom"};
120+
auto buffer = struct_pack::serialize(p);
121+
auto p2 = struct_pack::deserialize<example::person>(buffer);
122+
assert(p2);
123+
assert(p == p2.value());
124+
}
125+
{
126+
example2::person p{20, "tom"};
127+
auto buffer = struct_pack::serialize(p);
128+
auto p2 = struct_pack::deserialize<example2::person>(buffer);
129+
assert(p2);
130+
assert(p == p2.value());
131+
}
132+
{
133+
example2::person p{20, "tom"};
134+
auto buffer = struct_pack::serialize(p);
135+
auto p3 = struct_pack::deserialize<example2::person>(buffer);
136+
assert(p3);
137+
assert(p == p3.value());
138+
}
139+
{
140+
assert(struct_pack::get_type_code<example4::point>() !=
141+
struct_pack::get_type_code<example4::point2>());
142+
}
143+
{
144+
assert(struct_pack::get_type_code<example5::person>() ==
145+
struct_pack::get_type_code<example::person>());
146+
assert(struct_pack::get_type_code<example5::person>() ==
147+
struct_pack::get_type_code<example2::person>());
148+
assert(struct_pack::get_type_code<example5::person>() ==
149+
struct_pack::get_type_code<example3::person>());
150+
}
151+
}

src/struct_pack/tests/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ add_executable(struct_pack_test
1111
test_varint.cpp
1212
test_stream.cpp
1313
test_compatible.cpp
14+
test_non_aggregated_type.cpp
1415
main.cpp
1516
)
1617
add_test(NAME struct_pack_test COMMAND struct_pack_test)

0 commit comments

Comments
 (0)