Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
58927d2
Update README.md
neeraj31285 Sep 1, 2025
917e140
Update README.md
neeraj31285 Sep 1, 2025
40ceea7
Update README.md
neeraj31285 Sep 1, 2025
0769a13
Update README.md
neeraj31285 Sep 1, 2025
54a845a
Update README.md
neeraj31285 Sep 1, 2025
94a16ac
Update README.md
neeraj31285 Sep 1, 2025
fe62039
Update README.md
neeraj31285 Sep 1, 2025
f59c353
Update DESIGN_PRINCIPLES_AND_FEATURES.md
neeraj31285 Sep 1, 2025
2f33289
Update DESIGN_PRINCIPLES_AND_FEATURES.md
neeraj31285 Sep 1, 2025
4f06a05
Update DESIGN_PRINCIPLES_AND_FEATURES.md
neeraj31285 Sep 1, 2025
b6ab8d6
Update DESIGN_PRINCIPLES_AND_FEATURES.md
neeraj31285 Sep 1, 2025
07c47b3
Update DESIGN_PRINCIPLES_AND_FEATURES.md
neeraj31285 Sep 1, 2025
dbf042b
Update DESIGN_PRINCIPLES_AND_FEATURES.md
neeraj31285 Sep 2, 2025
8619259
Update RTL_SYNTAX_AND_SEMANTICS.md
neeraj31285 Sep 2, 2025
cb96675
Update RTL_SYNTAX_AND_SEMANTICS.md
neeraj31285 Sep 2, 2025
0aaa60e
Update CxxMirrorObjectTest.cpp
neeraj31285 Sep 2, 2025
be2f89b
Update README.md
neeraj31285 Sep 2, 2025
0ade581
Update README.md
neeraj31285 Sep 2, 2025
e6c3270
Update README.md
neeraj31285 Sep 2, 2025
92a9c63
Update LICENSE
neeraj31285 Sep 2, 2025
28f7687
Update README.md
neeraj31285 Sep 2, 2025
062cc88
Update DESIGN_PRINCIPLES_AND_FEATURES.md
neeraj31285 Sep 2, 2025
6f06ba8
Update DESIGN_PRINCIPLES_AND_FEATURES.md
neeraj31285 Sep 2, 2025
0526e9a
Update DESIGN_PRINCIPLES_AND_FEATURES.md
neeraj31285 Sep 2, 2025
6efb0ec
Update RTL_SYNTAX_AND_SEMANTICS.md
neeraj31285 Sep 2, 2025
20d0949
Update RTL_SYNTAX_AND_SEMANTICS.md
neeraj31285 Sep 2, 2025
383657d
Update RTL_SYNTAX_AND_SEMANTICS.md
neeraj31285 Sep 2, 2025
666097f
Update RTL_SYNTAX_AND_SEMANTICS.md
neeraj31285 Sep 2, 2025
6e45c43
Update RTL_SYNTAX_AND_SEMANTICS.md
neeraj31285 Sep 2, 2025
78d236d
Update README.md
neeraj31285 Sep 2, 2025
dabc31c
Update DESIGN_PRINCIPLES_AND_FEATURES.md
neeraj31285 Sep 2, 2025
dfc78d2
Update README.md
neeraj31285 Sep 3, 2025
f1300b7
Update README.md
neeraj31285 Sep 3, 2025
5c750a3
Update README.md
neeraj31285 Sep 3, 2025
1d9aef2
Update README.md
neeraj31285 Sep 3, 2025
04672fa
Update README.md
neeraj31285 Sep 3, 2025
b711885
Update README.md
neeraj31285 Sep 3, 2025
423aa25
Update README.md
neeraj31285 Sep 3, 2025
4874290
Update README.md
neeraj31285 Sep 3, 2025
f58f2c9
github action fix
neeraj31285 Sep 3, 2025
ae6a0c0
artifacts upload.
neeraj31285 Sep 3, 2025
584357d
Update README.md
neeraj31285 Sep 3, 2025
db6c4b3
Update README.md
neeraj31285 Sep 3, 2025
a004465
Update RTL_SYNTAX_AND_SEMANTICS.md
neeraj31285 Sep 3, 2025
a274309
fixed artifact upload.
neeraj31285 Sep 3, 2025
580a2aa
updated build action, ignore md
neeraj31285 Sep 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions .ygithub/workflows/build.yml → .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -23,7 +26,6 @@ jobs:
compiler: clang
- os: ubuntu-latest
compiler: msvc

steps:
- name: Checkout source
uses: actions/checkout@v4
Expand Down Expand Up @@ -51,4 +53,11 @@ jobs:

# Build
- name: Build
run: cmake --build build --config Release --parallel
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/
Original file line number Diff line number Diff line change
Expand Up @@ -362,4 +362,4 @@ namespace rtl_tests

EXPECT_EQ(cfunctorIds, stdfunctorIds);
}
}
}
29 changes: 13 additions & 16 deletions Design-Docs/DESIGN_PRINCIPLES_AND_FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Person>().method("getName").build(Person::getName);
rtl::type().member<Person>().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."*

Expand Down Expand Up @@ -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."*

Expand Down Expand Up @@ -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:

Expand Down
21 changes: 12 additions & 9 deletions Design-Docs/RTL_SYNTAX_AND_SEMANTICS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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."*

---

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2025 Neeraj Singh ([email protected])
Copyright (c) 2025 Neeraj Singh <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
39 changes: 19 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)&nbsp;[![C++20](https://img.shields.io/badge/C++-20-blue)](https://isocpp.org)&nbsp;[![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)&nbsp;[![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++

Expand All @@ -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++
Expand Down Expand Up @@ -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.
Expand Down
Loading