batman
This commit is contained in:
+69
@@ -0,0 +1,69 @@
|
|||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Linker files
|
||||||
|
*.ilk
|
||||||
|
|
||||||
|
# Debugger Files
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
*.so.*
|
||||||
|
|
||||||
|
# Fortran module files
|
||||||
|
*.mod
|
||||||
|
*.smod
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# Build directories
|
||||||
|
build/
|
||||||
|
Build/
|
||||||
|
build-*/
|
||||||
|
|
||||||
|
# CMake generated files
|
||||||
|
CMakeFiles/
|
||||||
|
CMakeCache.txt
|
||||||
|
cmake_install.cmake
|
||||||
|
Makefile
|
||||||
|
install_manifest.txt
|
||||||
|
compile_commands.json
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.log
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# vcpkg
|
||||||
|
vcpkg_installed/
|
||||||
|
|
||||||
|
# debug information files
|
||||||
|
*.dwo
|
||||||
|
|
||||||
|
# test output & cache
|
||||||
|
Testing/
|
||||||
|
.cache/
|
||||||
|
.idea
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
project(FuckAssReflection LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz)
|
||||||
|
FetchContent_MakeAvailable(json)
|
||||||
|
|
||||||
|
add_executable(reflection_example main.cpp)
|
||||||
|
target_link_libraries(reflection_example PRIVATE nlohmann_json::nlohmann_json)
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
#include <concepts>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
struct FieldOptions {
|
||||||
|
double min = 0;
|
||||||
|
double max = 0;
|
||||||
|
bool hasMin = false;
|
||||||
|
bool hasMax = false;
|
||||||
|
bool hidden = false;
|
||||||
|
bool transient = false;
|
||||||
|
bool readOnly = false;
|
||||||
|
std::string_view label;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FieldOps {
|
||||||
|
void (*save)(const void*, json&);
|
||||||
|
void (*load)(void*, const json&);
|
||||||
|
std::string (*show)(const void*);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct Codec {
|
||||||
|
static void Save(const void* value, json& out) {
|
||||||
|
out = *static_cast<const T*>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Load(void* value, const json& in) {
|
||||||
|
*static_cast<T*>(value) = in.get<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string Show(const void* value) {
|
||||||
|
if constexpr (std::same_as<T, std::string>) return *static_cast<const T*>(value);
|
||||||
|
else if constexpr (std::same_as<T, bool>) return *static_cast<const T*>(value) ? "true" : "false";
|
||||||
|
else return std::to_string(*static_cast<const T*>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr FieldOps ops{Save, Load, Show};
|
||||||
|
};
|
||||||
|
|
||||||
|
class FieldVisitor {
|
||||||
|
public:
|
||||||
|
template<class T>
|
||||||
|
void Field(std::string_view key, T& value, FieldOptions options = {}) {
|
||||||
|
Visit(key, &value, options, Codec<T>::ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonWriter final : public FieldVisitor {
|
||||||
|
public:
|
||||||
|
json Result() const { return result; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops) override {
|
||||||
|
if (!options.transient) ops.save(value, result[std::string(key)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
json result = json::object();
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonReader final : public FieldVisitor {
|
||||||
|
public:
|
||||||
|
explicit JsonReader(const json& source) : data(source) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops) override {
|
||||||
|
if (options.transient || !data.is_object()) return;
|
||||||
|
const auto it = data.find(std::string(key));
|
||||||
|
if (it != data.end()) ops.load(value, *it);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const json& data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConsoleInspector final : public FieldVisitor {
|
||||||
|
protected:
|
||||||
|
void Visit(std::string_view key, void* value, const FieldOptions& options, const FieldOps& ops) override {
|
||||||
|
if (options.hidden) return;
|
||||||
|
const auto label = options.label.empty() ? key : options.label;
|
||||||
|
std::cout << label << " = " << ops.show(value);
|
||||||
|
if (options.readOnly) std::cout << " [read only]";
|
||||||
|
if (options.hasMin || options.hasMax) {
|
||||||
|
std::cout << " [";
|
||||||
|
if (options.hasMin) std::cout << options.min;
|
||||||
|
std::cout << ", ";
|
||||||
|
if (options.hasMax) std::cout << options.max;
|
||||||
|
std::cout << "]";
|
||||||
|
}
|
||||||
|
std::cout << '\n';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Player {
|
||||||
|
std::string name = "Ada";
|
||||||
|
int health = 100;
|
||||||
|
float speed = 4.5f;
|
||||||
|
bool active = true;
|
||||||
|
std::string runtimeNote = "created at runtime";
|
||||||
|
|
||||||
|
void Reflect(FieldVisitor& visitor) {
|
||||||
|
visitor.Field("name", name);
|
||||||
|
visitor.Field("health", health, {.min = 0, .max = 100, .hasMin = true, .hasMax = true});
|
||||||
|
visitor.Field("speed", speed, {.min = 0, .hasMin = true, .label = "Move Speed"});
|
||||||
|
visitor.Field("active", active);
|
||||||
|
visitor.Field("internalId", runtimeNote, {.hidden = true});
|
||||||
|
visitor.Field("runtimeNote", runtimeNote, {.transient = true});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Player player;
|
||||||
|
JsonWriter writer;
|
||||||
|
player.Reflect(writer);
|
||||||
|
const json saved = writer.Result();
|
||||||
|
std::cout << "Saved JSON\n" << saved.dump(2) << "\n\n";
|
||||||
|
|
||||||
|
player.health = 1;
|
||||||
|
player.speed = 0;
|
||||||
|
JsonReader reader(saved);
|
||||||
|
player.Reflect(reader);
|
||||||
|
|
||||||
|
std::cout << "Console inspector\n";
|
||||||
|
ConsoleInspector inspector;
|
||||||
|
player.Reflect(inspector);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user