GCC Code Coverage Report


Directory: ./
File: include/hpp/util/serialization.hh
Date: 2025-05-17 13:07:10
Exec Total Coverage
Lines: 53 61 86.9%
Functions: 24 24 100.0%
Branches: 39 130 30.0%

Line Branch Exec Source
1 //
2 // Copyright (c) 2020 CNRS
3 // Authors: Joseph Mirabel
4 //
5
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are
8 // met:
9 //
10 // 1. Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 //
13 // 2. Redistributions in binary form must reproduce the above copyright
14 // notice, this list of conditions and the following disclaimer in the
15 // documentation and/or other materials provided with the distribution.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
28 // DAMAGE.
29
30 #ifndef HPP_UTIL_SERIALIZATION_HH
31 #define HPP_UTIL_SERIALIZATION_HH
32
33 #include <boost/archive/binary_iarchive.hpp>
34 #include <boost/archive/binary_oarchive.hpp>
35 #include <boost/archive/text_iarchive.hpp>
36 #include <boost/archive/text_oarchive.hpp>
37 #include <boost/archive/xml_iarchive.hpp>
38 #include <boost/archive/xml_oarchive.hpp>
39 #include <boost/preprocessor/comma_if.hpp>
40 #include <boost/preprocessor/facilities/is_empty.hpp>
41 #include <boost/serialization/export.hpp>
42 #include <boost/serialization/nvp.hpp>
43 #include <boost/serialization/shared_ptr.hpp>
44 #include <boost/serialization/utility.hpp>
45 #include <boost/version.hpp>
46 #include <hpp/util/config.hh>
47 #include <hpp/util/serialization-fwd.hh>
48
49 #define _HPP_SERIALIZATION_SPLIT_IMPLEMENT(type, archive, arg) \
50 template void type load<archive##_iarchive>( \
51 archive##_iarchive & ar, \
52 arg BOOST_PP_COMMA_IF(BOOST_PP_NOT(BOOST_PP_IS_EMPTY(arg))) \
53 const unsigned int ver); \
54 template void type save<archive##_oarchive>( \
55 archive##_oarchive & ar, \
56 BOOST_PP_IF(BOOST_PP_IS_EMPTY(arg), , const) \
57 arg BOOST_PP_COMMA_IF(BOOST_PP_NOT(BOOST_PP_IS_EMPTY(arg))) \
58 const unsigned int ver) \
59 BOOST_PP_IF(BOOST_PP_IS_EMPTY(arg), const, )
60
61 #define _HPP_SERIALIZATION_IMPLEMENT(type, archive, arg) \
62 template void type serialize<archive##_iarchive>( \
63 archive##_iarchive & ar, \
64 arg BOOST_PP_COMMA_IF(BOOST_PP_NOT(BOOST_PP_IS_EMPTY(arg))) \
65 const unsigned int ver); \
66 template void type serialize<archive##_oarchive>( \
67 archive##_oarchive & ar, \
68 arg BOOST_PP_COMMA_IF(BOOST_PP_NOT(BOOST_PP_IS_EMPTY(arg))) \
69 const unsigned int ver)
70
71 #define HPP_SERIALIZATION_SPLIT_IMPLEMENT(type) \
72 _HPP_SERIALIZATION_SPLIT_IMPLEMENT(type::, boost::archive::xml, ); \
73 _HPP_SERIALIZATION_SPLIT_IMPLEMENT(type::, boost::archive::text, ); \
74 _HPP_SERIALIZATION_SPLIT_IMPLEMENT(type::, boost::archive::binary, )
75
76 #define HPP_SERIALIZATION_IMPLEMENT(type) \
77 _HPP_SERIALIZATION_IMPLEMENT(type::, boost::archive::xml, ); \
78 _HPP_SERIALIZATION_IMPLEMENT(type::, boost::archive::text, ); \
79 _HPP_SERIALIZATION_IMPLEMENT(type::, boost::archive::binary, )
80
81 #define HPP_SERIALIZATION_FREE_IMPLEMENT(type) \
82 namespace boost { \
83 namespace serialization { \
84 _HPP_SERIALIZATION_IMPLEMENT(, archive::xml, type& t); \
85 _HPP_SERIALIZATION_IMPLEMENT(, archive::text, type& t); \
86 _HPP_SERIALIZATION_IMPLEMENT(, archive::binary, type& t); \
87 } \
88 }
89
90 #define HPP_SERIALIZATION_SPLIT_FREE_IMPLEMENT(type) \
91 namespace boost { \
92 namespace serialization { \
93 template <class Archive> \
94 void serialize(Archive& ar, type& t, const unsigned int version) { \
95 split_free(ar, t, version); \
96 } \
97 _HPP_SERIALIZATION_IMPLEMENT(, archive::xml, type& t); \
98 _HPP_SERIALIZATION_IMPLEMENT(, archive::text, type& t); \
99 _HPP_SERIALIZATION_IMPLEMENT(, archive::binary, type& t); \
100 } \
101 }
102
103 namespace hpp {
104 namespace serialization {
105 using boost::serialization::guid;
106 using boost::serialization::guid_defined;
107 using boost::serialization::make_nvp;
108
109 class holder_base {
110 public:
111 const char* classid;
112 14 virtual ~holder_base() = default;
113
114 protected:
115 7 holder_base(const char* classid) : classid(classid) {}
116 };
117
118 template <typename T>
119 class holder : public holder_base {
120 public:
121 T* t;
122 14 holder(T* t, const char* classid) : holder_base(classid), t(t) {}
123 10 holder(T* t) : holder(t, guid<T>()) {
124 static_assert(guid_defined<T>::value,
125 "You must use BOOST_CLASS_EXPORT_KEY on this class first.");
126 10 }
127
128 template <typename U>
129 1 explicit operator holder<U>() {
130
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return holder<U>(dynamic_cast<U*>(t), classid);
131 }
132 };
133
134 class archive_ptr_holder {
135 std::map<std::string, holder_base*> ptrs_;
136
137 public:
138 bool contains(const std::string& k) const { return ptrs_.count(k); }
139 template <typename T>
140 bool containsOfType(const std::string& k) const {
141 return (get<T>(k, false)) != NULL;
142 }
143
144 6 void insert(const std::string& k, holder_base* ptr) { ptrs_[k] = ptr; }
145 template <typename T>
146 10 void insert(const std::string& k, T* ptr) {
147
2/6
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
10 insert(k, (holder_base*)new holder<T>(ptr));
148 10 }
149 6 holder_base* get(const std::string& k, bool throwIfNotFound = false) const {
150
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 auto _ptr = ptrs_.find(k);
151
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if (_ptr == ptrs_.end()) {
152 if (!throwIfNotFound) return NULL;
153 throw std::invalid_argument("Pointer with name " + k + " not found.");
154 } else
155 6 return _ptr->second;
156 }
157 template <typename T>
158 3 T* get(const std::string& k, bool throwIfNotFound = false) const {
159 3 holder_base* hb(get(k, throwIfNotFound));
160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (hb == NULL) return NULL;
161 holder<T>* h;
162
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if ((h = dynamic_cast<holder<T>*>(hb)) == NULL) {
163 if (!throwIfNotFound) return NULL;
164 throw std::invalid_argument("Pointer with name " + k + " found of type " +
165 hb->classid + " but not of requested type " +
166 guid<T>() + ".");
167 }
168 3 return h->t;
169 }
170
171 template <typename Base, typename Child>
172 1 void insertChildClass(const std::string& k, Child* ptr) {
173 static_assert(guid_defined<Child>::value,
174 "You must use BOOST_CLASS_EXPORT_KEY on this class first.");
175
1/4
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1 insert(k, (holder_base*)new holder<Base>(ptr, guid<Child>()));
176 1 }
177 template <typename Base, typename Child>
178 1 Child* getChildClass(const std::string& k,
179 bool throwIfNotFound = false) const {
180
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 holder_base* hb(get(k, throwIfNotFound));
181
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (hb == NULL) return NULL;
182 holder<Base>* b;
183
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if ((b = dynamic_cast<holder<Base>*>(hb)) == NULL) {
184 if (!throwIfNotFound) return NULL;
185 throw std::invalid_argument("Pointer with name " + k + " found of type " +
186 hb->classid + " but not of requested type " +
187 guid<Child>() + ".");
188 }
189
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 holder<Child> c(*b);
190
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (c.t == NULL) {
191 if (!throwIfNotFound) return NULL;
192 throw std::invalid_argument("Pointer with name " + k + " found of type " +
193 hb->classid + " but not of requested type " +
194 guid<Child>() + ".");
195 }
196 1 return c.t;
197 1 }
198
199 22 virtual ~archive_ptr_holder() {
200
3/4
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 6 times.
✓ Branch 11 taken 11 times.
34 for (auto it : ptrs_) delete it.second;
201 22 }
202
203 protected:
204 template <typename Archive,
205 std::enable_if_t<Archive::is_saving::value, int> = 42>
206 2 void initialize_tpl(Archive& ar) // save
207 {
208 2 auto size(ptrs_.size());
209
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 ar << make_nvp("nrequires", size);
210 typedef std::pair<std::string, std::string> string_pair;
211
3/4
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 2 times.
4 for (auto it : ptrs_) {
212 string_pair
213
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 requires(it.first, it.second->classid);
214
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 ar << make_nvp("requires", requires);
215 }
216 2 }
217 template <typename Archive,
218 std::enable_if_t<!Archive::is_saving::value, int> = 42>
219 2 void initialize_tpl(Archive& ar) // read
220 {
221 decltype(ptrs_.size()) size;
222
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 ar >> make_nvp("nrequires", size);
223 typedef std::pair<std::string, std::string> string_pair;
224 2 string_pair pair;
225
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (decltype(size) i = 0; i < size; ++i) {
226
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 ar >> make_nvp("requires", pair);
227
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 holder_base* hb = get(pair.first, true);
228
3/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
2 if (pair.second != hb->classid)
229
7/14
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 1 times.
✗ Branch 21 not taken.
1 throw std::invalid_argument(
230 "Required pointer with name " + pair.first + " found of type " +
231 hb->classid + " but not of required type " + pair.second + ".");
232 }
233 2 }
234 };
235
236 template <typename archive_base, typename... parent_classes>
237 class archive_tpl : public archive_base,
238 public archive_ptr_holder,
239 public parent_classes... {
240 public:
241 8 inline void initialize() { initialize_tpl<archive_base>(*this); }
242
243 using archive_base::archive_base;
244 };
245
246 template <typename Archive>
247 1 archive_ptr_holder& cast(Archive& ar) {
248 1 return dynamic_cast<archive_ptr_holder&>(ar);
249 }
250 template <typename Archive>
251 archive_ptr_holder* cast(Archive* ar) {
252 return dynamic_cast<archive_ptr_holder*>(ar);
253 }
254
255 typedef archive_tpl<boost::archive::binary_iarchive> binary_iarchive;
256 typedef archive_tpl<boost::archive::binary_oarchive> binary_oarchive;
257
258 typedef archive_tpl<boost::archive::xml_iarchive> xml_iarchive;
259 typedef archive_tpl<boost::archive::xml_oarchive> xml_oarchive;
260
261 typedef archive_tpl<boost::archive::text_iarchive> text_iarchive;
262 typedef archive_tpl<boost::archive::text_oarchive> text_oarchive;
263 } // namespace serialization
264 } // namespace hpp
265
266 #endif // HPP_UTIL_SERIALIZATION_HH
267