diff --git a/.ygithub/workflows/build.yml b/.github/workflows/build.yml similarity index 80% rename from .ygithub/workflows/build.yml rename to .github/workflows/build.yml index a05d639e..68304f36 100644 --- a/.ygithub/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,15 +3,18 @@ name: RTL Build on: push: branches: [ release ] + paths-ignore: + - '**/*.md' pull_request: branches: [ release ] + paths-ignore: + - '**/*.md' workflow_dispatch: jobs: build: name: Build (${{ matrix.os }} / ${{ matrix.compiler }}) runs-on: ${{ matrix.os }} - strategy: matrix: os: [ubuntu-latest, windows-latest] @@ -23,7 +26,6 @@ jobs: compiler: clang - os: ubuntu-latest compiler: msvc - steps: - name: Checkout source uses: actions/checkout@v4 @@ -51,4 +53,11 @@ jobs: # Build - name: Build - run: cmake --build build --config Release --parallel \ No newline at end of file + run: cmake --build build --config Release --parallel + + # Upload artifacts per compiler + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: rtl-test-binaries-${{ matrix.compiler }} + path: bin/ \ No newline at end of file diff --git a/CxxRTLTestApplication/src/CxxMirrorTests/CxxMirrorObjectTest.cpp b/CxxRTLTestApplication/src/CxxMirrorTests/CxxMirrorObjectTest.cpp index 067ee245..570a8d33 100644 --- a/CxxRTLTestApplication/src/CxxMirrorTests/CxxMirrorObjectTest.cpp +++ b/CxxRTLTestApplication/src/CxxMirrorTests/CxxMirrorObjectTest.cpp @@ -362,4 +362,4 @@ namespace rtl_tests EXPECT_EQ(cfunctorIds, stdfunctorIds); } -} \ No newline at end of file +} diff --git a/Design-Docs/DESIGN_PRINCIPLES_AND_FEATURES.md b/Design-Docs/DESIGN_PRINCIPLES_AND_FEATURES.md index 80f110d0..a6702599 100644 --- a/Design-Docs/DESIGN_PRINCIPLES_AND_FEATURES.md +++ b/Design-Docs/DESIGN_PRINCIPLES_AND_FEATURES.md @@ -10,16 +10,16 @@ Instead, registration is explicit and lazy. For each registered type, RTL contributes **two lightweight entries** into its process-local tables: -* A **lambda wrapper** placed in a `static` `std::vector`, responsible for calling the actual function or constructor with perfect forwarding. -* A **raw function pointer** stored in a parallel `static` `std::vector`, used to detect and prevent redundant registrations. +* A **lambda wrapper** placed in a scoped `static` `std::vector` and is responsible for making the final call using the actual functor. +* A **raw function pointer** stored in a parallel scoped `static` `std::vector`, used to detect and prevent redundant registrations. -From there, `rtl::CxxMirror` does not hold onto heavyweight state. It is **as ordinary as any local variable** — you can construct one, keep it alive for the entire application, or discard it after a short-lived query. The same `rtl::CxxMirror` can be materialized again with the same or different set of types. RTL guarantees that **materializing the same registration sequence multiple times** (for example): +From there, `rtl::CxxMirror` does not hold onto heavyweight state. It is **as ordinary as any local variable** — you can construct one, keep it alive for the entire application, or discard it after a short-lived query. The `CxxMirror` can be materialized again with the same or different set of types. RTL guarantees that **materializing the same registration statement multiple times** (for example): ```cpp -rtl::type().member().method("getName").build(Person::getName); +rtl::type().member().method("getName").build(&Person::getName); ``` -will always yield **exactly the same metadata**, without ever admitting redundant lambdas or function pointers into the static tables. +will always yield **exactly the same metadata**, without ever admitting redundant lambdas or functors into the static tables. > *"Mirrors are **cheap and repeatable**: the metadata is stable, redundant entries are never entertained, and the user remains in full control of a mirror’s lifetime."* @@ -49,20 +49,17 @@ This is extremely unlikely, but not absolutely impossible — no system is perfe For every predictable failure case, RTL returns explicit error codes instead of throwing. RTL validates all critical assumptions before proceeding, ensuring predictable behavior and eliminating mid-operation surprises. +> *"Exceptions should never surprise you — in RTL, failures are explicit, validated, and reported as error codes, not as hidden runtime traps."* + --- ### 🔒 Const-By-Default Discipline -RTL enforces a *const-by-default* discipline. -All objects **created by RTL through reflection** are treated as immutable unless the caller explicitly requests mutation. - -This means: - -* **No accidental state changes** — reflected objects default to safe, immutable views. -* **Immediate clarity** — mutable access is visually deliberate in the code. -* **Defensive by design** — the default assumption is safety; mutation is always an opt-in. - -At the same time, RTL **respects the declared constness of external objects** (e.g., return values or user-provided instances). If an object is handed to RTL as `const` *(true-const)*, RTL will not attempt to override that contract. Only RTL-created objects guarantee that a logical `const_cast` is always safe. +RTL enforces a *const-by-default* discipline. All objects **created through reflection** start as *logically-const* — they default to immutability. If no const overload exists, RTL will **automatically fall back** to the non-const overload, since these objects were never originally declared `const`. Explicit `rtl::constCast()` is only required when both const and non-const overloads are present. + +The guiding principle is simple: reflective objects are safe by default, and any mutation must be a conscious, visible decision by the caller. + +At the same time, RTL strictly respects **true-const** objects (e.g., declared-`const` instances or const return values). Such objects remain immutable inside RTL — any attempt to force mutation results in predictable error code (`rtl::error::IllegalConstCast`). > *"RTL never mutates true-const objects, and for RTL-created ones it defaults to const, falling back only if needed — explicit rtl::constCast() is required when both overloads exist."* @@ -100,7 +97,7 @@ The key idea is that RTL doesn’t force you into a wrapper-first mindset. Inste #### ✨ The Mirror & The Reflection -> A client system hands off a `CxxMirror` to RTL — and RTL sees its reflection. +> *A client system hands off a `CxxMirror` to RTL — and RTL sees its reflection.* That’s it. The mirror is a **single object**, typically returned from a function like: diff --git a/Design-Docs/RTL_SYNTAX_AND_SEMANTICS.md b/Design-Docs/RTL_SYNTAX_AND_SEMANTICS.md index ad21568d..b1487991 100644 --- a/Design-Docs/RTL_SYNTAX_AND_SEMANTICS.md +++ b/Design-Docs/RTL_SYNTAX_AND_SEMANTICS.md @@ -32,22 +32,25 @@ Before registering anything, you need a central place to hold all reflection met }); ``` -Every registration you make using the builder pattern is collected into the `rtl::CxxMirror` as an `rtl::Function` object. The `CxxMirror` forms the backbone of RTL. Every type, function, or method you register ultimately gets encapsulated into this single object, serving as the gateway to query, introspect, and instantiate all registered types at runtime. +Every registration statement you add here is collected into the `rtl::CxxMirror` as an `rtl::Function` object. The `CxxMirror` forms the backbone of RTL. Every type, function, or method you register ultimately gets encapsulated into this single object, serving as the gateway to query, introspect, and instantiate all registered types at runtime. ### A few key points about managing this object -* **Dispensable by design** → The `rtl::CxxMirror` itself carries no hidden global state. You can define one central mirror, create multiple mirrors in different scopes, or even rebuild mirrors on demand. RTL imposes no restriction on how you manage its lifetime. +* ***Dispensable by design*** → The `CxxMirror` itself carries no hidden global state. You can define one central mirror, create multiple mirrors in different scopes, or even rebuild mirrors on demand. RTL imposes no restriction on how you manage its lifetime. -* **Duplicate registration is harmless** → Identical registrations always materialize the same metadata. If a canonical function pointer or constructor is already registered, it is not added again to the pointer/lambda table — the metadata simply refers back to the existing entry. +* ***Duplicate registration is harmless*** → Identical registrations always materialize the same metadata. If a canonical function pointer is already registered, it is not added again to the lambda/functor table — the metadata simply refers back to the existing entry. -* **Thread-safety guaranteed by RTL** → No matter how you choose to manage mirrors (singleton, multiple, or transient), RTL itself guarantees synchronized, race-free registration and access across threads. +* ***Thread-safety guaranteed by RTL*** → No matter how you choose to manage mirrors (singleton, multiple, or transient), RTL itself guarantees synchronized, race-free registration and access across threads. -* **Overhead is deliberate** → Each registration carries a small cost in memory and initialization time. This overhead exists to provide thread-safety, robustness, and avoidance of redundant registration, and should be considered when creating many mirrors or registering large numbers of types. +* ***Overhead is deliberate*** → Each registration carries a small cost in memory and initialization time. Concretely: + * Every registration statement acquires a lock on the functor table. + * It checks whether the function or lambda is already present. + * If not, it adds the new entry to the lambda table and updates the functor table. + +This ensures thread-safety and prevents redundant entries. While negligible for isolated registrations, this cost can accumulate when creating many mirrors or registering large numbers of types. -You are free to manage mirrors however your design requires: one mirror for the whole program, multiple mirrors for modularity, or transient mirrors in local scopes. RTL is designed to work correctly in all cases, while keeping its small, deliberate overhead in mind to ensure safety and efficiency. - -👉 **Tip** -> ***When many types need registering for the full application lifetime, a singleton `rtl::CxxMirror` is your safest and simplest choice — one instance, zero surprises.*** +👉 Bottom Line +> *"Manage `CxxMirror` however your design requires — singleton, multiple, or transient. Each registration incurs a lock and table lookup, but the cost is negligible in normal use and only noticeable when scaling to very large numbers of types."* --- diff --git a/LICENSE b/LICENSE index 313073f0..7dc0e44c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2025 Neeraj Singh (reflectcxx@outlook.com) +Copyright (c) 2025 Neeraj Singh Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index dc0f8b34..24e877d4 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,33 @@ -# Reflection Template Library (RTL) - Modern C++ Reflection Framework +# Reflection Template Library - Modern C++ Reflection Framework -**Reflection Template Library (RTL)** is a lightweight C++ runtime reflection library that enables introspection and dynamic manipulation of types *(not limited to user-defined)* — allowing you to access, modify, and invoke objects at runtime without compile-time type knowledge. +*Reflection Template Library (RTL)* is a lightweight C++ runtime reflection library that enables introspection and dynamic manipulation of ***Types*** — allowing you to access, modify, and invoke objects at runtime without compile-time type knowledge. -RTL is implemented as a static library that organizes type-safe function pointers into tables `(std::vector)`, with each pointer wrapped in a lambda. This design enables constant-time `O(1)` lookup and efficient runtime access. +RTL is implemented as a *static library* that organizes function pointers into `std::vector` tables, with each functor wrapped in a lambda. This design enables constant-time `O(1)` lookups while ensuring type-safe and efficient runtime access. + +[![CMake](https://img.shields.io/badge/CMake-Enabled-brightgreen)](https://cmake.org) [![C++20](https://img.shields.io/badge/C++-20-blue)](https://isocpp.org) [![RTL Build](https://github.com/ReflectCxx/ReflectionTemplateLibrary-CPP/actions/workflows/build.yml/badge.svg?branch=release)](https://github.com/ReflectCxx/ReflectionTemplateLibrary-CPP/actions/workflows/build.yml?query=branch%3Arelease) [![License: MIT](https://img.shields.io/badge/License-MIT-green)](LICENSE) -[![CMake](https://img.shields.io/badge/CMake-Enabled-brightgreen)](https://cmake.org) -[![C++20](https://img.shields.io/badge/C++-20-blue)](https://isocpp.org) -[![License: MIT](https://img.shields.io/badge/License-MIT-green)](LICENSE) ## What RTL Brings to Your Code -[![Design Principles & Features](https://img.shields.io/badge/Doc-Design%20Principles%20%26%20Features-blue)](./Design-Docs/DESIGN_PRINCIPLES_AND_FEATURES.md) -[![RTL Syntax & Semantics](https://img.shields.io/badge/Doc-RTL_at_a_Glance:_Syntax_&_Semantics-blueviolet)](./Design-Docs/RTL_SYNTAX_AND_SEMANTICS.md) -* **Runtime Reflection for C++** – Introspect and manipulate objects dynamically, similar to Java or .NET, but with modern C++ idioms. +* ***Runtime Reflection for C++*** – Introspect and manipulate objects dynamically, similar to Java or .NET, but with modern C++ idioms. + +* ***Single Source of Truth*** – All metadata lives in one immutable `rtl::CxxMirror`, ensuring a consistent, thread-safe, duplication-free, and deterministic view of reflection data. -* **Single Source of Truth** – All metadata lives in one immutable `rtl::CxxMirror`, ensuring a consistent, thread-safe, duplication-free, and deterministic view of reflection data. +* ***Non-Intrusive & Macro-Free*** – Register reflection metadata externally via a clean builder pattern; no macros, base classes, or global registries. -* **Non-Intrusive & Macro-Free** – Register reflection metadata externally via a clean builder pattern; no macros, base classes, or global registries. +* ***Zero-Overhead by Design*** – Metadata is registered and resolved only when used. Reflection introduces no cost beyond the features you explicitly employ. -* **Const-By-Default Safety** – Everything is immutable unless explicitly mutable, preventing unintended side-effects in reflective code. +* ***Exception-Free Surface*** – All predictable failures return error codes; no hidden throws. -* **Exception-Free Surface** – All predictable failures return error codes; no hidden throws. +* ***Deterministic Lifetimes*** – Automatic ownership tracking of `Heap` and `Stack` instances with zero hidden deep copies. -* **Deterministic Lifetimes** – Automatic ownership tracking of `Heap` and `Stack` instances with zero hidden deep copies. +* ***Cross-Compiler Consistency*** – Pure standard C++20, with no compiler extensions or conditional branching on compiler differences. -* **Cross-Compiler Consistency** – Pure standard C++20, with no compiler extensions or conditional branching on compiler differences. +* ***Tooling-Friendly Architecture*** – Reflection data is encapsulated in a single immutable, lazily-initialized object that can be shared with tools and frameworks without compile-time type knowledge — ideal for serializers, debuggers, test frameworks, scripting engines, and editors. -* **Tooling-Friendly Architecture** – Reflection data is encapsulated in a single immutable, lazily-initialized object that can be shared with tools and frameworks without compile-time type knowledge — ideal for serializers, debuggers, test frameworks, scripting engines, and editors. + +[![Design Features](https://img.shields.io/badge/Doc-Design%20Features-blue)](./Design-Docs/DESIGN_PRINCIPLES_AND_FEATURES.md) +[![RTL Syntax & Semantics](https://img.shields.io/badge/Doc-Syntax_&_Semantics-blueviolet)](./Design-Docs/RTL_SYNTAX_AND_SEMANTICS.md) ## A Quick Preview: Reflection That Looks and Feels Like C++ @@ -46,8 +47,6 @@ auto cxx_mirror = rtl::CxxMirror({ With just this much, you’ve registered your types and unlocked full runtime reflection. The `cxx_mirror` object is your gateway to query, introspect, and instantiate types at runtime — all without compile-time knowledge of those types, without strict static coupling. -RTL’s API is deliberately minimal and mirrors C++ syntax, but with strict runtime validation. Each reflective operation checks types, ownership, and errors, ensuring semantics that follow C++ rules while preventing undefined behavior through explicit error codes. - ***Without reflection:*** ```c++ @@ -128,8 +127,8 @@ RTL doesn’t invent a new paradigm — it extends C++ itself. You create object * ✅ **Namespace Support** 🗂️ – Group and reflect under namespaces. * ✅ **Reflected Returns** 🔍 – Access return values whose types are unknown at compile time. Validate against the expected type and use them as if the type was known all along. * ✅ **Smart Pointer Reflection** 🔗 – Reflect `std::shared_ptr` and `std::unique_ptr`, transparently access the underlying type, and benefit from automatic lifetime management with full sharing and cloning semantics. -* ✅ **Conservative Conversions** 🛡️ – Safely reinterpret reflected values without hidden costs. For example: treat an `int` as a `char`, or a `std::string` as a `std::string_view` / `const char*` — with no hidden copies and only safe, non-widening POD conversions. -* ✅ **Materialize New Types** 🔄 – Convert a reflected type `A` into type `B` if they are implicitly convertible. Define custom conversions at registration to make them available automatically. *(In Progress)* +* 🟨 **Conservative Conversions** 🛡️ – Safely reinterpret reflected values without hidden costs. For example: treat an `int` as a `char`, or a `std::string` as a `std::string_view` / `const char*` — with no hidden copies and only safe, non-widening POD conversions. *(In Progress)* +* 🟨 **Materialize New Types** 🔄 – Convert a reflected type `A` into type `B` if they are implicitly convertible. Define custom conversions at registration to make them available automatically. *(In Progress)* * 🚧 **STL Wrapper Support** 📦 – Extended support for wrappers like `std::optional` and `std::reference_wrapper`. Return them, forward them as parameters, and handle them seamlessly. *(In Progress)* * 🚧 **Relaxed Argument Matching** ⚙️ – Flexible parameter matching for reflective calls, enabling intuitive conversions and overload resolution. *(In Progress)* * ❌ **Property Reflection**: Planned.