This commit is contained in:
2026-10-30 18:50:47 +01:00
commit d11044df6f
3 changed files with 218 additions and 0 deletions
+69
View File
@@ -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
+13
View File
@@ -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)
+136
View File
@@ -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);
}