Skip to content

Commit 3a2e6a9

Browse files
committed
cppjson::Object
Signed-off-by: TymianekPL <[email protected]>
1 parent 4e6c9c1 commit 3a2e6a9

File tree

5 files changed

+128
-54
lines changed

5 files changed

+128
-54
lines changed

Test/Test.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
int main()
55
{
6-
cppjson::JsonObject object{};
7-
object.As<std::string>() = "Purr world!";
6+
cppjson::Object object{};
7+
std::println("{}", object);
8+
object["test"] = "Hello World";
89
std::println("{}", object);
910
}

cppjson/include/cppjson/cppjson.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
#pragma once
1+
#pragma once
22

3-
namespace cppjson {
4-
void hello_world();
3+
namespace cppjson
4+
{
5+
void hello_world();
56
} // namespace cppjson

cppjson/include/cppjson/object.hpp

Lines changed: 101 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
#pragma once
22

3-
#include <cstdint>
4-
#include <cstddef>
53
#include <algorithm>
6-
#include <string>
74
#include <cstddef>
5+
#include <cstdint>
86
#include <format>
7+
#include <string>
98
#include <unordered_map>
9+
#include <functional>
10+
#include <concepts>
1011

1112
namespace cppjson
1213
{
@@ -22,7 +23,7 @@ namespace cppjson
2223

2324
class JsonObject
2425
{
25-
public:
26+
public:
2627
explicit JsonObject();
2728
~JsonObject();
2829

@@ -31,52 +32,105 @@ namespace cppjson
3132

3233
template <typename T>
3334
const T& As() const noexcept(false);
34-
private:
35+
36+
private:
3537
JsonType _dataType{};
3638
std::byte* _dataStorage{};
3739

3840
void Destroy();
3941
template <typename T>
40-
T& DangerousAs() noexcept { return *std::launder(reinterpret_cast<T*>(this->_dataStorage)); }
42+
T& DangerousAs() noexcept
43+
{
44+
return *std::launder(reinterpret_cast<T*>(this->_dataStorage));
45+
}
4146
template <typename T>
42-
const T& DangerousAs() const noexcept { return *std::launder(reinterpret_cast<T*>(this->_dataStorage)); }
47+
const T& DangerousAs() const noexcept
48+
{
49+
return *std::launder(reinterpret_cast<T*>(this->_dataStorage));
50+
}
4351

4452
friend struct std::formatter<cppjson::JsonObject>;
4553
};
4654

4755
class Object
4856
{
49-
public:
57+
public:
5058
explicit Object() = default;
5159
Object(const Object&) = default;
52-
Object(Object&&) = default;
60+
Object(Object&&) = default;
5361
Object& operator=(const Object&) = default;
5462
Object& operator=(Object&&) = default;
5563
~Object() = default;
5664

57-
template <typename T>
58-
T& operator[](const std::string& key)
65+
class ObjectProxy
5966
{
60-
return this->_nodes[key].As<T>();
61-
}
62-
template <typename T>
63-
const T& operator[](const std::string& key) const
67+
public:
68+
explicit ObjectProxy(JsonObject& object) : _object(std::ref(object)) {}
69+
70+
template <typename T>
71+
operator T&()
72+
{
73+
return this->_object.get().As<T>();
74+
}
75+
76+
template <typename T>
77+
operator const T&() const
78+
{
79+
return this->_object.get().As<T>();
80+
}
81+
82+
template <typename T>
83+
T& operator=(T&& assignment)
84+
{
85+
return static_cast<T&>(*this) = std::forward<T>(assignment);
86+
}
87+
88+
template <std::size_t N>
89+
std::string& operator=(const char(&str)[N])
90+
{
91+
return static_cast<std::string&>(*this) = std::string{ str };
92+
}
93+
private:
94+
std::reference_wrapper<JsonObject> _object;
95+
};
96+
97+
98+
class ConstObjectProxy
99+
{
100+
public:
101+
explicit ConstObjectProxy(const JsonObject& object) : _object(std::ref(object)) {}
102+
template <typename T>
103+
operator const T&() const
104+
{
105+
return this->_object.get().As<T>();
106+
}
107+
private:
108+
std::reference_wrapper<const JsonObject> _object;
109+
};
110+
111+
ObjectProxy operator[](const std::string& key)
112+
{
113+
return ObjectProxy{ this->_nodes[key] };
114+
}
115+
116+
ConstObjectProxy operator[](const std::string& key) const
64117
{
65118
if (!this->_nodes.contains(key)) throw std::logic_error("Invalid key" + key);
66-
return this->_nodes.at(key).As<T>();
119+
120+
return ConstObjectProxy{ this->_nodes.at(key) };
67121
}
68-
private:
122+
123+
private:
69124
std::unordered_map<std::string, JsonObject> _nodes{};
125+
126+
friend struct std::formatter<cppjson::Object>;
70127
};
71-
}
128+
} // namespace cppjson
72129

73-
template<>
130+
template <>
74131
struct std::formatter<cppjson::JsonObject>
75132
{
76-
constexpr auto parse(std::format_parse_context& context)
77-
{
78-
return context.begin();
79-
}
133+
constexpr auto parse(std::format_parse_context& context) { return context.begin(); }
80134

81135
auto format(const cppjson::JsonObject& object, std::format_context& context) const
82136
{
@@ -85,9 +139,32 @@ struct std::formatter<cppjson::JsonObject>
85139
case cppjson::JsonType::Null: return std::format_to(context.out(), "null");
86140
case cppjson::JsonType::Bool: return std::format_to(context.out(), "{}", object.DangerousAs<bool>());
87141
case cppjson::JsonType::Number: return std::format_to(context.out(), "{}", object.DangerousAs<double>());
88-
case cppjson::JsonType::String: return std::format_to(context.out(), "{}", object.DangerousAs<std::string>());
142+
case cppjson::JsonType::String: return std::format_to(context.out(), "\"{}\"", object.DangerousAs<std::string>());
89143
}
90144

91145
throw std::logic_error("Unknown type");
92146
}
93147
};
148+
149+
template <>
150+
struct std::formatter<cppjson::Object>
151+
{
152+
constexpr auto parse(std::format_parse_context& context) { return context.begin(); }
153+
154+
auto format(const cppjson::Object& object, std::format_context& context) const
155+
{
156+
std::string built = "{ ";
157+
for (const auto& [key, value] : object._nodes)
158+
built += std::format("\"{}\": {}, ", key, value);
159+
160+
if (!object._nodes.empty()) // remove trailing commas
161+
{
162+
built.pop_back();
163+
built.pop_back();
164+
built += " }";
165+
}
166+
else built += "}";
167+
168+
return std::format_to(context.out(), "{}", built);
169+
}
170+
};

cppjson/src/cppjson.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include <cppjson/cppjson.hpp>
1+
#include <cppjson/cppjson.hpp>
22
#include <print>
33

44
void cppjson::hello_world() { std::println("Hewwo wowld"); }

cppjson/src/object.cpp

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
#include "cppjson/object.hpp"
1+
#include "cppjson/object.hpp"
22
#include <new>
33
#include <stdexcept>
44

5-
constexpr std::size_t DataStorageSize = std::max({ sizeof(std::string), sizeof(cppjson::Object), sizeof(double), sizeof(bool) });
5+
constexpr std::size_t DataStorageSize = std::max({sizeof(std::string), sizeof(cppjson::Object), sizeof(double), sizeof(bool)});
66

7-
cppjson::JsonObject::JsonObject()
8-
: _dataStorage(static_cast<std::byte*>(::operator new(DataStorageSize)))
9-
{
10-
}
7+
cppjson::JsonObject::JsonObject() : _dataStorage(static_cast<std::byte*>(::operator new(DataStorageSize))) {}
118

129
cppjson::JsonObject::~JsonObject()
1310
{
@@ -28,97 +25,95 @@ void cppjson::JsonObject::Destroy(void)
2825
}
2926
}
3027

31-
template<>
28+
template <>
3229
std::string& cppjson::JsonObject::As<std::string>() noexcept(false)
3330
{
3431
if (this->_dataType == JsonType::Null)
3532
{
3633
this->_dataType = JsonType::String;
37-
return *new(this->_dataStorage) std::string{};
34+
return *new (this->_dataStorage) std::string{};
3835
}
3936

4037
if (this->_dataType != JsonType::String) throw std::logic_error("Cannot convert this object to a string");
4138
return DangerousAs<std::string>();
4239
}
4340

44-
template<>
41+
template <>
4542
double& cppjson::JsonObject::As<double>() noexcept(false)
4643
{
4744
if (this->_dataType == JsonType::Null)
4845
{
4946
this->_dataType = JsonType::Number;
50-
return *new(this->_dataStorage) double{};
47+
return *new (this->_dataStorage) double{};
5148
}
5249

5350
if (this->_dataType != JsonType::Number) throw std::logic_error("Cannot convert this object to a double");
5451
return DangerousAs<double>();
5552
}
5653

57-
template<>
54+
template <>
5855
bool& cppjson::JsonObject::As<bool>() noexcept(false)
5956
{
6057
if (this->_dataType == JsonType::Null)
6158
{
6259
this->_dataType = JsonType::Bool;
63-
return *new(this->_dataStorage) bool{};
60+
return *new (this->_dataStorage) bool{};
6461
}
6562

6663
if (this->_dataType != JsonType::Bool) throw std::logic_error("Cannot convert this object to a bool");
6764
return DangerousAs<bool>();
6865
}
6966

70-
template<>
67+
template <>
7168
cppjson::Object& cppjson::JsonObject::As<cppjson::Object>() noexcept(false)
7269
{
7370
if (this->_dataType == JsonType::Null)
7471
{
7572
this->_dataType = JsonType::Object;
76-
return *new(this->_dataStorage) cppjson::Object{};
73+
return *new (this->_dataStorage) cppjson::Object{};
7774
}
7875

7976
if (this->_dataType != JsonType::Object) throw std::logic_error("Cannot convert this object to a bool");
8077
return DangerousAs<cppjson::Object>();
8178
}
8279

83-
template<>
80+
template <>
8481
std::nullptr_t& cppjson::JsonObject::As<std::nullptr_t>() noexcept(false)
8582
{
86-
if (std::exchange(this->_dataType, JsonType::Null) == JsonType::Null)
87-
return DangerousAs<std::nullptr_t>();
83+
if (std::exchange(this->_dataType, JsonType::Null) == JsonType::Null) return DangerousAs<std::nullptr_t>();
8884

89-
return *new(this->_dataStorage) std::nullptr_t{};
85+
return *new (this->_dataStorage) std::nullptr_t{};
9086
}
9187

92-
template<>
88+
template <>
9389
const std::string& cppjson::JsonObject::As<std::string>() const noexcept(false)
9490
{
9591
if (this->_dataType != JsonType::String) throw std::logic_error("Cannot convert this object to a string");
9692
return DangerousAs<std::string>();
9793
}
9894

99-
template<>
95+
template <>
10096
const double& cppjson::JsonObject::As<double>() const noexcept(false)
10197
{
10298
if (this->_dataType != JsonType::Number) throw std::logic_error("Cannot convert this object to a double");
10399
return DangerousAs<double>();
104100
}
105101

106-
template<>
102+
template <>
107103
const bool& cppjson::JsonObject::As<bool>() const noexcept(false)
108104
{
109105
if (this->_dataType != JsonType::Bool) throw std::logic_error("Cannot convert this object to a bool");
110106
return DangerousAs<bool>();
111107
}
112108

113-
template<>
109+
template <>
114110
const cppjson::Object& cppjson::JsonObject::As<cppjson::Object>() const noexcept(false)
115111
{
116112
if (this->_dataType != JsonType::Object) throw std::logic_error("Cannot convert this object to an object");
117113
return DangerousAs<cppjson::Object>();
118114
}
119115

120-
121-
template<>
116+
template <>
122117
const std::nullptr_t& cppjson::JsonObject::As<std::nullptr_t>() const noexcept(false)
123118
{
124119
if (this->_dataType != JsonType::Null) throw std::logic_error("Cannot convert this object to a null");

0 commit comments

Comments
 (0)