Skip to content

Commit fe14af7

Browse files
committed
Make custom variant to speed up compilation
1 parent 1ec3b4a commit fe14af7

File tree

3 files changed

+178
-52
lines changed

3 files changed

+178
-52
lines changed

include/json2cpp/constexpr_json.hpp

+163-44
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222
SOFTWARE.
2323
*/
2424

25+
// Important note: the types in this file are only intended for compile-time construction
26+
// but consteval doesn't exist in C++17
27+
2528
#ifndef CONSTEXPR_JSON_HPP_INCLUDED
2629
#define CONSTEXPR_JSON_HPP_INCLUDED
2730

@@ -30,7 +33,6 @@ SOFTWARE.
3033
#include <cstdint>
3134
#include <stdexcept>
3235
#include <string_view>
33-
#include <variant>
3436

3537
namespace constexpr_json {
3638
template<typename First, typename Second> struct pair
@@ -58,23 +60,138 @@ template<typename T> struct span
5860
const T *end_;
5961
};
6062

61-
struct json;
63+
template<typename CharType> struct basic_json;
64+
template<typename CharType> using basic_array_t = span<basic_json<CharType>>;
65+
template<typename CharType> using basic_value_pair_t = pair<std::basic_string_view<CharType>, basic_json<CharType>>;
66+
template<typename CharType> using basic_object_t = span<basic_value_pair_t<CharType>>;
6267

63-
using array_t = span<json>;
64-
using value_pair_t = pair<std::string_view, json>;
65-
using object_t = span<value_pair_t>;
6668
using binary_t = span<std::uint8_t>;
6769

68-
using data_t = std::
69-
variant<std::monostate, bool, binary_t, array_t, object_t, std::int64_t, std::uint64_t, double, std::string_view>;
70+
template<typename CharType> struct data_variant
71+
{
72+
struct monostate
73+
{
74+
};
75+
76+
union value_t {
77+
monostate empty_;
78+
bool bool_;
79+
binary_t binary_;
80+
basic_array_t<CharType> array_;
81+
basic_object_t<CharType> object_;
82+
std::int64_t int64_t_;
83+
std::uint64_t uint64_t_;
84+
double double_;
85+
std::basic_string_view<CharType> string_view_;
86+
87+
constexpr explicit value_t() : empty_{} {}
88+
constexpr explicit value_t(monostate) : value_t() {}
89+
constexpr explicit value_t(bool b) : bool_{ b } {}
90+
constexpr explicit value_t(binary_t b) : binary_{ b } {}
91+
constexpr explicit value_t(basic_array_t<CharType> a) : array_{ a } {}
92+
constexpr explicit value_t(basic_object_t<CharType> o) : object_{ o } {}
93+
constexpr explicit value_t(std::int64_t i) : int64_t_{ i } {}
94+
constexpr explicit value_t(std::uint64_t i) : uint64_t_{ i } {}
95+
constexpr explicit value_t(double d) : double_{ d } {}
96+
constexpr explicit value_t(std::basic_string_view<CharType> s) : string_view_{ s } {}
97+
};
98+
99+
enum struct selected_type { empty, boolean, binary, array, object, integer, uinteger, floating_point, string };
100+
101+
value_t value{ monostate{} };
102+
selected_type selected{ selected_type::empty };
103+
104+
// letting these be implicit measurably improves construction performance
105+
106+
constexpr data_variant() = default;
107+
// cppcheck-suppress noExplicitConstructor
108+
constexpr data_variant(monostate) : data_variant() {}
109+
// cppcheck-suppress noExplicitConstructor
110+
constexpr data_variant(bool b) : value{ b }, selected{ selected_type::boolean } {}
111+
// cppcheck-suppress noExplicitConstructor
112+
constexpr data_variant(basic_array_t<CharType> a) : value{ a }, selected{ selected_type::array } {}
113+
// cppcheck-suppress noExplicitConstructor
114+
constexpr data_variant(basic_object_t<CharType> o) : value{ o }, selected{ selected_type::object } {}
115+
// cppcheck-suppress noExplicitConstructor
116+
constexpr data_variant(std::int64_t i) : value{ i }, selected{ selected_type::integer } {}
117+
// cppcheck-suppress noExplicitConstructor
118+
constexpr data_variant(std::uint64_t i) : value{ i }, selected{ selected_type::uinteger } {}
119+
// cppcheck-suppress noExplicitConstructor
120+
constexpr data_variant(double d) : value{ d }, selected{ selected_type::floating_point } {}
121+
// cppcheck-suppress noExplicitConstructor
122+
constexpr data_variant(std::basic_string_view<CharType> s) : value{ s }, selected{ selected_type::string } {}
123+
124+
[[nodiscard]] constexpr const bool *get_if_boolean() const noexcept
125+
{
126+
if (selected == selected_type::boolean) {
127+
return &value.bool_;
128+
} else {
129+
return nullptr;
130+
}
131+
}
132+
133+
[[nodiscard]] constexpr const basic_array_t<CharType> *get_if_array() const noexcept
134+
{
135+
if (selected == selected_type::array) {
136+
return &value.array_;
137+
} else {
138+
return nullptr;
139+
}
140+
}
141+
[[nodiscard]] constexpr const basic_object_t<CharType> *get_if_object() const noexcept
142+
{
143+
if (selected == selected_type::object) {
144+
return &value.object_;
145+
} else {
146+
return nullptr;
147+
}
148+
}
149+
[[nodiscard]] constexpr const std::int64_t *get_if_integer() const noexcept
150+
{
151+
if (selected == selected_type::integer) {
152+
return &value.int64_t_;
153+
} else {
154+
return nullptr;
155+
}
156+
}
157+
[[nodiscard]] constexpr const std::uint64_t *get_if_uinteger() const noexcept
158+
{
159+
if (selected == selected_type::uinteger) {
160+
return &value.uint64_t_;
161+
} else {
162+
return nullptr;
163+
}
164+
}
165+
166+
[[nodiscard]] constexpr const double *get_if_floating_point() const noexcept
167+
{
168+
if (selected == selected_type::floating_point) {
169+
return &value.double_;
170+
} else {
171+
return nullptr;
172+
}
173+
}
174+
175+
[[nodiscard]] constexpr const std::basic_string_view<CharType> *get_if_string() const noexcept
176+
{
177+
if (selected == selected_type::string) {
178+
return &value.string_view_;
179+
} else {
180+
return nullptr;
181+
}
182+
}
183+
};
70184

71-
struct json
185+
template<typename CharType> struct basic_json
72186
{
187+
using data_t = data_variant<CharType>;
188+
73189
struct iterator
74190
{
75-
constexpr explicit iterator(const json &value, std::size_t index = 0) : parent_value_(&value), index_{ index } {}
191+
constexpr explicit iterator(const basic_json &value, std::size_t index = 0) : parent_value_(&value), index_{ index }
192+
{}
76193

77-
constexpr const json &operator*() const
194+
constexpr const basic_json &operator*() const
78195
{
79196
if (parent_value_->is_array()) {
80197
return (*parent_value_)[index_];
@@ -85,14 +202,14 @@ struct json
85202
}
86203
}
87204

88-
constexpr const json *operator->() const { return &(*(*this)); }
205+
constexpr const basic_json *operator->() const { return &(*(*this)); }
89206

90207
constexpr std::size_t index() const { return index_; }
91208

92-
constexpr const json &value() const { return *(*this); }
209+
constexpr const basic_json &value() const { return *(*this); }
93210

94211

95-
constexpr std::string_view key() const
212+
constexpr std::basic_string_view<CharType> key() const
96213
{
97214
if (parent_value_->is_object()) {
98215
return std::next(parent_value_->object_data().begin(), static_cast<std::ptrdiff_t>(index_))->first;
@@ -153,7 +270,7 @@ struct json
153270
return *this;
154271
}
155272

156-
const json *parent_value_{ nullptr };
273+
const basic_json *parent_value_{ nullptr };
157274
std::size_t index_{ 0 };
158275
};
159276

@@ -170,14 +287,14 @@ struct json
170287
constexpr std::size_t size() const noexcept
171288
{
172289
if (is_null()) { return 0; }
173-
if (is_object()) { return std::get_if<object_t>(&data)->size(); }
290+
if (is_object()) { return data.get_if_object()->size(); }
174291

175-
if (is_array()) { return std::get_if<array_t>(&data)->size(); }
292+
if (is_array()) { return data.get_if_array()->size(); }
176293

177294
return 1;
178295
}
179296

180-
constexpr const json &operator[](const std::size_t idx) const
297+
constexpr const basic_json &operator[](const std::size_t idx) const
181298
{
182299
if (const auto &children = array_data(); idx < children.size()) {
183300
return *std::next(children.begin(), static_cast<std::ptrdiff_t>(idx));
@@ -195,7 +312,7 @@ struct json
195312
return end();
196313
}
197314

198-
constexpr const json &operator[](const std::string_view key) const
315+
constexpr const basic_json &operator[](const std::string_view key) const
199316
{
200317
const auto &children = object_data();
201318

@@ -221,82 +338,80 @@ struct json
221338
}
222339
}
223340

224-
constexpr const array_t &array_data() const
341+
constexpr const auto &array_data() const
225342
{
226-
if (const auto *result = std::get_if<array_t>(&data); result != nullptr) {
343+
if (const auto *result = data.get_if_array(); result != nullptr) {
227344
return *result;
228345
} else {
229346
throw std::runtime_error("value is not an array type");
230347
}
231348
}
232349

233-
constexpr const object_t &object_data() const
350+
constexpr const auto &object_data() const
234351
{
235-
if (const auto *result = std::get_if<object_t>(&data); result != nullptr) {
352+
if (const auto *result = data.get_if_object(); result != nullptr) {
236353
return *result;
237354
} else {
238355
throw std::runtime_error("value is not an object type");
239356
}
240357
}
241358

242-
constexpr static json object() { return json{ object_t{} }; }
243-
244-
constexpr static json array() { return json{ array_t{} }; }
245-
359+
constexpr static basic_json object() { return basic_json{ data_t{ basic_object_t<CharType>{} } }; }
360+
constexpr static basic_json array() { return basic_json{ data_t{ basic_array_t<CharType>{} } }; }
246361

247362
template<typename Type> constexpr Type get() const
248363
{
249364
if constexpr (std::is_same_v<Type, std::uint64_t> || std::is_same_v<Type, std::int64_t>) {
250-
if (const auto *uint_value = std::get_if<std::uint64_t>(&data); uint_value != nullptr) {
365+
if (const auto *uint_value = data.get_if_uinteger(); uint_value != nullptr) {
251366
return Type(*uint_value);
252-
} else if (const auto *value = std::get_if<std::int64_t>(&data); value != nullptr) {
367+
} else if (const auto *value = data.get_if_integer(); value != nullptr) {
253368
return Type(*value);
254369
}
255370
} else if constexpr (std::is_same_v<Type, double>) {
256-
if (const auto *value = std::get_if<double>(&data); value != nullptr) { return *value; }
371+
if (const auto *value = data.get_if_floating_point(); value != nullptr) { return *value; }
257372
} else if constexpr (std::is_same_v<Type, std::string_view>) {
258-
if (const auto *value = std::get_if<std::string_view>(&data); value != nullptr) { return *value; }
373+
if (const auto *value = data.get_if_string(); value != nullptr) { return *value; }
259374
} else if constexpr (std::is_same_v<Type, bool>) {
260-
if (const auto *value = std::get_if<bool>(&data); value != nullptr) { return *value; }
375+
if (const auto *value = data.get_if_boolean(); value != nullptr) { return *value; }
261376
} else {
262377
throw std::runtime_error("Unexpected type for get()");
263378
}
264379

265380
throw std::runtime_error("Incorrect type for get()");
266381
}
267382

268-
constexpr bool is_object() const noexcept { return std::holds_alternative<object_t>(data); }
383+
constexpr bool is_object() const noexcept { return data.selected == data_t::selected_type::object; }
269384

270-
constexpr bool is_array() const noexcept { return std::holds_alternative<array_t>(data); }
385+
constexpr bool is_array() const noexcept { return data.selected == data_t::selected_type::array; }
271386

272-
constexpr bool is_string() const noexcept { return std::holds_alternative<std::string_view>(data); }
387+
constexpr bool is_string() const noexcept { return data.selected == data_t::selected_type::string; }
273388

274-
constexpr bool is_boolean() const noexcept { return std::holds_alternative<bool>(data); }
389+
constexpr bool is_boolean() const noexcept { return data.selected == data_t::selected_type::boolean; }
275390

276391
constexpr bool is_structured() const noexcept { return is_object() || is_array(); }
277392

278393
constexpr bool is_number() const noexcept { return is_number_integer() || is_number_float(); }
279394

280395
constexpr double as_number_float() const
281396
{
282-
if (const double *value = std::get_if<double>(&data); value != nullptr) {
397+
if (const double *value = data.get_if_floating_point(); value != nullptr) {
283398
return *value;
284399
} else {
285400
throw std::runtime_error("Not a float type");
286401
}
287402
}
288403
constexpr double as_boolean() const
289404
{
290-
if (const bool *value = std::get_if<bool>(&data); value != nullptr) {
405+
if (const bool *value = data.get_if_boolean(); value != nullptr) {
291406
return *value;
292407
} else {
293408
throw std::runtime_error("Not a boolean type");
294409
}
295410
}
296411

297-
constexpr std::string_view as_string() const
412+
constexpr auto as_string() const
298413
{
299-
if (const auto *value = std::get_if<std::string_view>(&data); value != nullptr) {
414+
if (const auto *value = data.get_if_string(); value != nullptr) {
300415
return *value;
301416
} else {
302417
throw std::runtime_error("Not a string type");
@@ -307,15 +422,15 @@ struct json
307422

308423
constexpr bool is_number_integer() const noexcept { return is_number_signed() || is_number_unsigned(); }
309424

310-
constexpr bool is_number_signed() const noexcept { return std::holds_alternative<std::int64_t>(data); }
425+
constexpr bool is_number_signed() const noexcept { return data.selected == data_t::selected_type::integer; }
311426

312-
constexpr bool is_number_unsigned() const noexcept { return std::holds_alternative<std::uint64_t>(data); }
427+
constexpr bool is_number_unsigned() const noexcept { return data.selected == data_t::selected_type::uinteger; }
313428

314-
constexpr bool is_number_float() const noexcept { return std::holds_alternative<double>(data); }
429+
constexpr bool is_number_float() const noexcept { return data.selected == data_t::selected_type::floating_point; }
315430

316-
constexpr bool is_null() const noexcept { return std::holds_alternative<std::monostate>(data); }
431+
constexpr bool is_null() const noexcept { return data.selected == data_t::selected_type::empty; }
317432

318-
constexpr bool is_binary() const noexcept { return std::holds_alternative<binary_t>(data); }
433+
constexpr bool is_binary() const noexcept { return data.selected == data_t::selected_type::binary; }
319434

320435
constexpr bool is_primitive() const noexcept
321436
{
@@ -325,6 +440,10 @@ struct json
325440
data_t data;
326441
};
327442

443+
using json = basic_json<char>;
444+
using object_t = basic_object_t<char>;
445+
using value_pair_t = basic_value_pair_t<char>;
446+
using array_t = basic_array_t<char>;
328447
}// namespace constexpr_json
329448

330449
#endif

0 commit comments

Comments
 (0)