diff options
| -rw-r--r-- | .envrc | 1 | ||||
| -rw-r--r-- | .gitignore | 8 | ||||
| -rw-r--r-- | GNUmakefile | 59 | ||||
| -rw-r--r-- | flake.lock | 61 | ||||
| -rw-r--r-- | flake.nix | 19 | ||||
| -rw-r--r-- | src/json.cpp | 69 | ||||
| -rw-r--r-- | src/json.h | 40 | ||||
| -rw-r--r-- | src/json.hpp | 28 | ||||
| -rwxr-xr-x | test.sh | 3 | ||||
| -rw-r--r-- | test/main.cpp | 110 |
10 files changed, 398 insertions, 0 deletions
@@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66ccce8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.cache +.clang-format +.direnv +build +compile_commands.json +include +json_test +lib
\ No newline at end of file diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..b8aab00 --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,59 @@ +# Source +OBJS_DIR := ./build +SOURCE_DIR := ./src +LIB_DIR := ./lib +INCLUDE_DIR := ./include +TEST_SRC_DIR := ./test +out ?= +INSTALL_DIR = $(out) + +# Flags +CXX := g++ +CXXFLAGS := -std=c++23 # -fsanitize=address -g +LINKFLAGS := #-static-libasan +TEST_CXXFLAGS := -std=c++23 -I$(INCLUDE_DIR) -fsanitize=address -g +TEST_LINKFLAGS := -L$(LIB_DIR) -ltehjson -static-libasan + +# Outputs +LIB := $(LIB_DIR)/libtehjson.so +TEST := json_test + +# Files +SOURCE_FILES := $(wildcard $(SOURCE_DIR)/*.cpp) +SOURCE_HEADERS := $(wildcard $(SOURCE_DIR)/*.h $(SOURCE_DIR)/*.hpp) +OBJS := $(subst $(SOURCE_DIR),$(OBJS_DIR), $(patsubst %.cpp,%.o,$(SOURCE_FILES))) +INCLUDE_HEADERS := $(subst $(SOURCE_DIR),$(INCLUDE_DIR), $(SOURCE_HEADERS)) +TEST_SOURCE := $(wildcard $(TEST_SRC_DIR)/*.cpp) +TEST_HEADERS := $(wildcard $(TEST_SRC_DIR)/*.h) +TEST_OBJS = $(subst $(TEST_SRC_DIR),$(OBJS_DIR), $(patsubst %.cpp,%.o,$(TEST_SOURCE))) + +# Main lib +$(LIB): $(INCLUDE_HEADERS) $(OBJS) + $(CXX) $(OBJS) $(CXXFLAGS) $(LINKFLAGS) -shared -o $(LIB) + +$(OBJS_DIR)/%.o: $(SOURCE_DIR)/%.cpp $(SOURCE_DIR)/%.h + $(CXX) $(CXXFLAGS) -c $< -o $@ + +$(INCLUDE_DIR)/%.h: $(SOURCE_DIR)/%.h lib + cp $< $@ +$(INCLUDE_DIR)/%.hpp: $(SOURCE_DIR)/%.hpp lib + cp $< $@ + +# Test binary +$(TEST): $(TEST_OBJS) $(LIB) + $(CXX) $(TEST_OBJS) $(TEST_CXXFLAGS) $(TEST_LINKFLAGS) -o $(TEST) + +$(OBJS_DIR)/%.o: $(TEST_SRC_DIR)/%.cpp $(TEST_SOURCE) $(TEST_HEADERS) + $(CXX) $(TEST_CXXFLAGS) -c $< -o $@ + + +# Phony +.PHONY: clean +clean: + rm -f $(LIB) + rm -f $(OBJS_DIR)/*.o + rm -f $(TEST) + +test: $(TEST) + +$(OBJS_DIR)/json.o: $(SOURCE_DIR)/json.hpp diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..032fb8b --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1751274312, + "narHash": "sha256-/bVBlRpECLVzjV19t5KMdMFWSwKLtb5RyXdjz3LJT+g=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "50ab793786d9de88ee30ec4e4c24fb4236fc2674", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..18ad6d2 --- /dev/null +++ b/flake.nix @@ -0,0 +1,19 @@ +{ + description = "default c++ flake"; + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11"; + flake-utils.url = "github:numtide/flake-utils"; + }; + outputs = { self, nixpkgs, ... }@inputs: + inputs.flake-utils.lib.eachDefaultSystem (system: let + pkgs = nixpkgs.legacyPackages.${system}; + in { + devShells.default = pkgs.mkShell { + nativeBuildInputs = [ + pkgs.gcc + pkgs.gnumake + pkgs.clang-tools + ]; + }; + }); +} diff --git a/src/json.cpp b/src/json.cpp new file mode 100644 index 0000000..c11ca6e --- /dev/null +++ b/src/json.cpp @@ -0,0 +1,69 @@ +#include "json.h" + +#include <cstddef> +#include <stdexcept> + +namespace TehJSON +{ + JSON::JSON() + :children() + { + } + + JSON::~JSON() + { + } + + std::string JSON::leafType() + { + if(!isLeaf) + throw std::runtime_error("Node is not a leaf!"); + throw std::runtime_error("Not implemented yet"); + } + + std::string JSON::getSerialized() + { + return _getSerialized(0); + } + std::string JSON::_getSerialized(int currIndent) + { + if(isLeaf) + return dataSerializer(data); + + std::string serialized = "{\n"; + + int childrenLeft = childCount(); + for(auto [childName, child]: children) + { + childrenLeft--; + for(int i = 0; i < currIndent + 1; i++) + serialized += '\t'; + serialized += "\"" + childName + "\": "; + serialized += child._getSerialized(currIndent + 1); + if(childrenLeft != 0) + serialized += ','; + serialized += '\n'; + } + for(int i = 0; i < currIndent; i++) + serialized += '\t'; + serialized += "}"; + + return serialized; + } + + JSON& JSON::operator[](std::string name) + { + if(isLeaf) + throw std::runtime_error("Node is a leaf!"); + // if(children.count(name) == 0) + // throw std::out_of_range("Child \"" + name + "\" does not exist"); + return children[name]; + } + + size_t JSON::childCount() + { + if(isLeaf) + throw std::runtime_error("Node is a leaf!"); + return children.size(); + } +} diff --git a/src/json.h b/src/json.h new file mode 100644 index 0000000..d1d7938 --- /dev/null +++ b/src/json.h @@ -0,0 +1,40 @@ +#include <cstddef> +#include <map> +#include <memory> +#include <string> + +namespace TehJSON +{ + class JSON + { + public: + JSON(); + JSON(const JSON& other) = default; + ~JSON(); + + // Leaf methods + template <typename T> + T& get(); + template <typename T> + void set(T value); + std::string leafType(); + std::string getSerialized(); + std::string _getSerialized(int currIndent); + template <typename T> + static std::string serializeData(std::shared_ptr<void> data); + + // Non leaf methods + JSON& operator[](std::string name); + size_t childCount(); + // std::vector<std::string> childNames(); + private: + bool isLeaf = false; + + // Leaf data fields + std::shared_ptr<void> data; + std::string (*dataSerializer)(std::shared_ptr<void>); + + // Not leaf data fields + std::map<std::string, JSON> children; + }; +} diff --git a/src/json.hpp b/src/json.hpp new file mode 100644 index 0000000..6f19ddd --- /dev/null +++ b/src/json.hpp @@ -0,0 +1,28 @@ +#include "json.h" + +#include <stdexcept> +#include <iostream> + +namespace TehJSON +{ + + template<typename T> + T& JSON::get() + { + if(!isLeaf) + throw std::runtime_error("Node is not a leaf!"); + + return *static_cast<T*>(data.get()); + } + + template<typename T> + void JSON::set(T value) + { + if(children.size() != 0) + throw std::runtime_error("Node is not a leaf (has children already)!"); + isLeaf = true; + dataSerializer = JSON::serializeData<T>; + data = std::make_shared<T>(value); + } + +} @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +make test && LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH ./json_test $@ diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..9430503 --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,110 @@ +#include <json.hpp> + +#include <iostream> +#include <memory> + +using std::cout, std::endl; + +class TestClass { +private: + int id; + // std::string* testData; + +public: + // 1. Default Constructor: Sets initial "empty" state [20, 24] + TestClass() : id(-1)/*, testData(new std::string("default"))*/ { + std::cout << "Default Constructor called" << std::endl; + } + + // 2. Parameterized Constructor: For dependency injection/direct testing [24, 25] + TestClass(int id/*, std::string val*/) : id(id)/*, testData(new std::string(val))*/ { + std::cout << "Parameterized Constructor (id: " << id << ")" << std::endl; + } + + // 3. Copy Constructor (Rule of 5): Deep copies resource [14, 23] + TestClass(const TestClass& other) : id(other.id+1)/*, testData(new std::string(*other.testData))*/ { + std::cout << "Copy Constructor called" << id << std::endl; + } + + // 4. Move Constructor (Rule of 5): Transfers ownership of resource [14, 16] + TestClass(TestClass&& other) noexcept : id(other.id+1)/*, testData(other.testData)*/ { + // other.testData = nullptr; + other.id = -1; + std::cout << "Move Constructor called" << id << std::endl; + } + + // 5. Destructor: Essential for RAII and preventing memory leaks [14, 29] + ~TestClass() { + // delete testData; + std::cout << "Destructor called" << id << std::endl; + } + + // Assignment Operators (Standard Rule of 5) + TestClass& operator=(const TestClass& other) { + if (this == &other) return *this; + // delete testData; + id = other.id + 1; + // testData = new std::string(*other.testData); + std::cout << "Assignment op 1 called" << id << std::endl; + return *this; + } + + TestClass& operator=(TestClass&& other) noexcept { + if (this == &other) return *this; + // delete testData; + id = other.id + 1; + // testData = other.testData; + // other.testData = nullptr; + std::cout << "Assignment op 2 called" << id << std::endl; + return *this; + } + + // Helper for verification [18, 21] + int getId() const { return id; } + // std::string getData() const { return testData ? *testData : "null"; } +}; + + +template <> std::string TehJSON::JSON::serializeData<int>(std::shared_ptr<void> data) +{ + return std::to_string(*static_cast<int*>(data.get())); +} +template <> std::string TehJSON::JSON::serializeData<float>(std::shared_ptr<void> data) +{ + return std::to_string(*static_cast<float*>(data.get())); +} +template <> std::string TehJSON::JSON::serializeData<TestClass>(std::shared_ptr<void> data) +{ + return "test class"; +} +template <> std::string TehJSON::JSON::serializeData<std::string>(std::shared_ptr<void> data) +{ + return '"' + *static_cast<std::string*>(data.get()) + '"'; +} + + +int main() +{ + TehJSON::JSON json; + // TestClass test1; + // json["test1"].set(test1); + json["test_string"].set<std::string>("stringy"); + json["test_int"].set<int>(123); + json["test_float"].set<float>(51.8); + json["test_object"]["test_int"].set<int>(100); + json["test_object"]["test_float"].set<float>(100); + + cout << json.getSerialized() << endl; + + // std::shared_ptr<void> test; + // test = std::make_shared<TestClass>(1); + // test = std::make_shared<TestClass>(2); + // test = std::make_shared<std::string>(std::string("test")); + // std::string& testRef = *(std::string*)test.get(); + // cout << *(std::string*)test.get() << endl; + // testRef += " test2"; + // cout << *(std::string*)test.get() << endl; + + + cout << "abc" << endl; +} |
