Skip to content

Commit

Permalink
Merge pull request #2 from st235/feature/value_type
Browse files Browse the repository at this point in the history
Replace polymorphism with union type `json::Json`
  • Loading branch information
st235 authored Jun 28, 2024
2 parents 49f7e2d + 7abf48a commit 5aacd6b
Show file tree
Hide file tree
Showing 27 changed files with 615 additions and 794 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ FetchContent_MakeAvailable(googletest)
enable_testing()

add_executable(jsonc_tests
tests/json_null_tests.cpp

tests/json_token_reader_tests.cpp
tests/json_array_tests.cpp
tests/json_boolean_tests.cpp
Expand Down
358 changes: 353 additions & 5 deletions include/json.h
Original file line number Diff line number Diff line change
@@ -1,16 +1,364 @@
#ifndef __JSONC_JSON_H__
#define __JSONC_JSON_H__

#include <memory>
#include <string>
#include <optional>

#include "json_value.h"
#include "json_definitions.h"
#include "json_visitor.h"

namespace json {

std::unique_ptr<JsonValue> FromJson(const std::string& json);
class Json {
private:
struct ValueContainer {
public:
enum {
kTypeBool,
kTypeNumber,
kTypeString,
kTypeArray,
kTypeObject,
kTypeNull,
} type;

std::string ToJson(JsonValue* json);
union {
bool_t _bool;
number_t _number;
string_t* _string;
array_t* _array;
object_t* _object;
} value;

ValueContainer() noexcept:
type(kTypeNull) {
value._object = nullptr;
}

explicit ValueContainer(bool_t raw_value) noexcept:
type(kTypeBool) {
value._bool = raw_value;
}

explicit ValueContainer(number_t raw_value) noexcept:
type(kTypeNumber) {
value._number = raw_value;
}

ValueContainer(const string_t& raw_value) noexcept:
type(kTypeString) {
value._string = new string_t(raw_value);
}

ValueContainer(string_t&& raw_value) noexcept:
type(kTypeString) {
value._string = new string_t(std::move(raw_value));
}

ValueContainer(const array_t& raw_value) noexcept:
type(kTypeArray) {
value._array = new array_t(raw_value);
}

ValueContainer(array_t&& raw_value) noexcept:
type(kTypeArray) {
value._array = new array_t(std::move(raw_value));
}

ValueContainer(const object_t& raw_value) noexcept:
type(kTypeObject) {
value._object = new object_t(raw_value);
}

ValueContainer(object_t&& raw_value) noexcept:
type(kTypeObject) {
value._object = new object_t(std::move(raw_value));
}

ValueContainer(const ValueContainer& that) noexcept:
type(that.type) {
copyContainerValue(that);
}

ValueContainer& operator=(const ValueContainer& that) noexcept {
if (this != &that) {
type = that.type;
copyContainerValue(that);
}

return *this;
}

ValueContainer(ValueContainer&& that) noexcept:
type(that.type) {
swapContainerValue(std::move(that));
}

ValueContainer& operator=(ValueContainer&& that) noexcept {
if (this != &that) {
type = that.type;
swapContainerValue(std::move(that));
}

return *this;
}

~ValueContainer() {
switch (type) {
case kTypeNull:
case kTypeBool:
case kTypeNumber:
break;
case kTypeString:
delete value._string;
break;
case kTypeArray:
delete value._array;
break;
case kTypeObject:
delete value._object;
break;
}
}

private:
void copyContainerValue(const ValueContainer& that) {
switch (that.type) {
case kTypeNull:
value._object = nullptr;
break;
case kTypeBool:
value._bool = that.value._bool;
break;
case kTypeNumber:
value._number = that.value._number;
break;
case kTypeString:
value._string = new string_t(*that.value._string);
break;
case kTypeArray:
value._array = new array_t(*that.value._array);
break;
case kTypeObject:
value._object = new object_t(*that.value._object);
break;
}
}

void swapContainerValue(ValueContainer&& that) {
switch (that.type) {
case kTypeNull:
value._object = nullptr;
break;
case kTypeBool:
value._bool = that.value._bool;
break;
case kTypeNumber:
value._number = that.value._number;
break;
case kTypeString:
value._string = new string_t(std::move(*that.value._string));
break;
case kTypeArray:
value._array = new array_t(std::move(*that.value._array));
break;
case kTypeObject:
value._object = new object_t(std::move(*that.value._object));
break;
}
}
};

static const Json VALUE_NULL;

ValueContainer _container;

public:
static const Json& null();
static Json array();
static Json object();

static std::optional<Json> fromJson(const std::string& json);
static std::string toJson(const Json& json);

Json() noexcept: _container() {}
Json(bool_t value) noexcept: _container(value) {}
Json(number_t value) noexcept: _container(value) {}
Json(const string_t& value) noexcept: _container(value) {}
Json(string_t&& value) noexcept: _container(std::move(value)) {}
Json(const array_t& value) noexcept: _container(value) {}
Json(array_t&& value) noexcept: _container(std::move(value)) {}
Json(const object_t& value) noexcept: _container(value) {}
Json(object_t&& value) noexcept: _container(std::move(value)) {}

Json(const Json& that) noexcept:
_container(that._container) {
}

Json& operator=(const Json& that) noexcept {
if (this != &that) {
_container = that._container;
}
return *this;
}

Json(Json&& that) noexcept:
_container(std::move(that._container)) {
}

Json& operator=(Json&& that) noexcept {
if (this != &that) {
_container = std::move(that._container);
}
return *this;
}

bool operator==(const Json& that) const {
if (_container.type != that._container.type) {
return false;
}

switch (_container.type) {
case ValueContainer::kTypeNull:
return _container.value._object == that._container.value._object;
case ValueContainer::kTypeBool:
return _container.value._bool == that._container.value._bool;
case ValueContainer::kTypeNumber:
return _container.value._number == that._container.value._number;
case ValueContainer::kTypeString:
return *_container.value._string == *that._container.value._string;
case ValueContainer::kTypeArray:
return *_container.value._array == *that._container.value._array;
case ValueContainer::kTypeObject:
return *_container.value._object == *that._container.value._object;
}
}

bool operator!=(const Json& that) const {
return !operator==(that);
}

explicit operator bool() const {
switch (_container.type) {
case ValueContainer::kTypeBool:
return _container.value._bool != false;
case ValueContainer::kTypeNumber:
return _container.value._number != 0.0;
case ValueContainer::kTypeString:
return _container.value._string->length() > 0;
case ValueContainer::kTypeArray:
return _container.value._array->size() > 0;
case ValueContainer::kTypeNull:
case ValueContainer::kTypeObject:
return _container.value._object != nullptr;
}
}

inline bool isNull() const {
return _container.type == ValueContainer::kTypeNull;
}

inline bool isBool() const {
return _container.type == ValueContainer::kTypeBool;
}

inline bool isNumber() const {
return _container.type == ValueContainer::kTypeNumber;
}

inline bool isString() const {
return _container.type == ValueContainer::kTypeString;
}

inline bool isArray() const {
return _container.type == ValueContainer::kTypeArray;
}

inline bool isObject() const {
return _container.type == ValueContainer::kTypeObject;
}

inline bool_t asBool() const {
return _container.value._bool;
}

inline number_t asNumber() const {
return _container.value._number;
}

inline const string_t& asString() const {
return *_container.value._string;
}

inline const array_t& asArray() const {
return *_container.value._array;
}

inline const object_t& asObject() const {
return *_container.value._object;
}

const Json& operator[](size_t index) const {
return (*_container.value._array)[index];
}

const Json& operator[](const std::string& key) const {
return (*_container.value._object)[key];
}

Json& operator[](const std::string& key) {
return (*_container.value._object)[key];
}

void add(const Json& that) {
_container.value._array->push_back(that);
}

void add(Json&& that) {
_container.value._array->emplace_back(std::move(that));
}

void put(const std::string& key, const Json& json) {
(*_container.value._object)[key] = json;
}

void put(const std::string& key, Json&& json) {
(*_container.value._object)[key] = std::move(json);
}

size_t size() const {
switch (_container.type) {
case ValueContainer::kTypeArray:
return _container.value._array->size();
case ValueContainer::kTypeObject:
return _container.value._object->size();
}
}

void accept(JsonVisitor& visitor) const {
switch (_container.type) {
case ValueContainer::kTypeNull:
visitor.visitNull();
break;
case ValueContainer::kTypeBool:
visitor.visitBoolean(asBool());
break;
case ValueContainer::kTypeNumber:
visitor.visitNumber(asNumber());
break;
case ValueContainer::kTypeString:
visitor.visitString(asString());
break;
case ValueContainer::kTypeArray:
visitor.visitArray(asArray());
break;
case ValueContainer::kTypeObject:
visitor.visitObject(asObject());
break;
}
}

~Json() = default;
};

} // namespace json

Expand Down
Loading

0 comments on commit 5aacd6b

Please sign in to comment.