Skip to content

Commit 60bfd92

Browse files
authored
Merge pull request #99 from ReflectCxx/develop
CxxMirror: disabled implicit copy-ctor. polished comments.
2 parents cb59ab4 + 76066cd commit 60bfd92

7 files changed

Lines changed: 101 additions & 80 deletions

File tree

.github/workflows/build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ on:
1010
- '**/*.md'
1111
push:
1212
branches: [ release ]
13+
paths-ignore:
14+
- '**/*.md'
1315
workflow_dispatch:
1416

1517
jobs:

.github/workflows/coverage.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ on:
77
- '**/*.md'
88
push:
99
branches: [ release ]
10+
paths-ignore:
11+
- '**/*.md'
1012
workflow_dispatch:
1113

1214
jobs:

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ rtl::CxxMirror& cxx::mirror() {
8282
return cxx_mirror;
8383
}
8484
```
85+
`cxx_mirror` is a immutable, stack-allocated, value-type object and is safe to copy.
86+
However, when used as a singleton (as shown above), implicit copies (e.g., `auto mirror = cxx::mirror()`) can unintentionally violate the singleton semantics.
87+
To prevent this, the `rtl::CxxMirror`'s copy constructor is restricted to avoid such unintended duplication.
88+
8589
### RTL in action:
8690

8791
**[Explore the demo code](https://github.com/ReflectCxx/RTL-Demo)**

RTLTestRunApp/src/CxxMirrorTests/CxxMirrorObjectTest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ namespace rtl_tests
4747
// Two mirrors constructed from same set of registrations must serialize identically.
4848
// Confirms stability of metadata and deterministic JSON output.
4949
rtl::CxxMirror mirror = cxx_mirror();
50-
rtl::CxxMirror mirror0 = mirror;
50+
rtl::CxxMirror mirror0(mirror);
5151
mirrorStr0 = rtl::CxxMirrorToJson::toJson(mirror0);
5252
}
5353
std::string mirrorStr1;

RTLTestRunApp/src/RObjectTests/RObjectReflecting_stdSharedPtr.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,6 @@
1111
using namespace test_utils;
1212
using namespace rtl;
1313

14-
namespace {
15-
16-
static rtl::CxxMirror cxx_mirror()
17-
{
18-
static rtl::CxxMirror m = rtl::CxxMirror({
19-
rtl::type().record<int>("int").build(),
20-
rtl::type().record<Node>("Node").build()
21-
});
22-
return m;
23-
}
24-
}
2514

2615
namespace rtl::unit_test
2716
{

ReflectionTemplateLib/rtl/inc/CxxMirror.h

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,52 +15,57 @@
1515

1616
namespace rtl
1717
{
18-
/* @class CxxMirror
19-
* Provides the primary interface to access registered functions and methods by name.
20-
* This is the single point of access to the entire reflection system.
18+
/**
19+
* @class CxxMirror
2120
*
22-
* All type registrations happen during object construction.
21+
* @brief Primary interface for accessing all registered types, functions, and methods by name.
2322
*
24-
* Objects of this class are regular stack-allocated objects (non-singleton) and are destroyed automatically when they go out of scope.
25-
* Copy constructor and assignment operator are deleted, instances can only be passed by reference or wrapped in a smart pointer.
23+
* CxxMirror serves as the single point of entry to the entire reflection system.
24+
* All type, function, and method registrations occur during object construction.
2625
*
27-
* All inherited members are properly destroyed when the object is destroyed, except for the *functor containers*.
26+
* Instances of this class are regular, stack-allocated value-type objects.
27+
* The copy constructor is explicit, and the assignment operator is deleted.
2828
*
29-
* Notes on Functor Storage:
30-
* - Functor containers have static lifetime and are not part of this class or its base class.
31-
* - This class (and its base) store only `Function` objects, which serve as hash-keys to look up actual functors.
32-
* - Registering the same functor multiple times across different `CxxMirror` instances will not duplicate the functor in the container.
33-
* - However, each `CxxMirror` instance will maintain its own unique `Function` hash-keys, even for the same functor.
34-
* - Within a single `CxxMirror` object, registering the same functor multiple times is ignored (no duplicate `Function` hash-keys).
29+
* The class stores only metadata in the form of strings and PODs.
30+
* It does not own or manage any heap-allocated or static-lifetime resources.
3531
*
36-
* Summary:
37-
* - Functor objects are shared and static.
38-
* - `Function` keys are per-instance.
39-
* - Functor storage remains unaffected by the number of `CxxMirror` instances.
40-
*/
32+
* Function pointers provided during registration are cached separately and are
33+
* decoupled from this class. Each CxxMirror instance maintains its own metadata
34+
* that references the registered function pointers.
35+
*
36+
* As a result, the same function pointer may be associated with different string
37+
* keys across different CxxMirror instances without duplicating the underlying
38+
* function pointer storage.
39+
*
40+
* Within a single CxxMirror instance, attempting to register the same functor
41+
* multiple times is ignored, and a warning is emitted to the console.
42+
*/
4143
class CxxMirror : public detail::CxxReflection
4244
{
4345
public:
4446

45-
CxxMirror(CxxMirror&&) = default;
46-
CxxMirror(const CxxMirror&) = default;
47+
// avoids implicit move.
48+
explicit CxxMirror(CxxMirror&&) = default;
49+
50+
// avoids implicit copy.
51+
explicit CxxMirror(const CxxMirror&) = default;
4752

48-
// Constructs CxxMirror using a set of Function objects. All other constructors are disabled.
53+
// Constructs CxxMirror using a set of Function objects.
4954
explicit CxxMirror(const std::vector<Function>& pFunctions);
5055

51-
// Returns a Record containing function hash-keys for the given record ID.
52-
std::optional<Record> getRecord(const std::size_t pRecordId) const;
56+
// Returns a valid Record if the type is found by id; otherwise, std::nullopt.
57+
std::optional<Record> getRecord(const traits::uid_t pRecordId) const;
5358

54-
// Returns a Record containing function hash-keys for the given record name.
59+
// Returns a valid Record if the type is found by name in default namespace group; otherwise, std::nullopt.
5560
std::optional<Record> getRecord(const std::string& pRecordName) const;
5661

57-
// Returns a Record containing function hash-keys for the given record name (overloaded for namespace support).
62+
// Returns a valid Record if the type is found by name in the given namespace group; otherwise, std::nullopt.
5863
std::optional<Record> getRecord(const std::string& pNameSpaceName, const std::string& pRecordName) const;
5964

60-
// Returns a Function object for the given function name (non-member function).
65+
// Returns a valid Function if found by name in default namespace group; otherwise, std::nullopt.
6166
std::optional<Function> getFunction(const std::string& pFunctionName) const;
6267

63-
// Returns a Function object for the given function name, within the specified namespace.
68+
// Returns a valid Function if found by name in the given namespace group; otherwise, std::nullopt.
6469
std::optional<Function> getFunction(const std::string& pNameSpaceName, const std::string& pFunctionName) const;
6570
};
6671
}

ReflectionTemplateLib/rtl/src/CxxMirror.cpp

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,80 +15,99 @@
1515

1616
namespace rtl
1717
{
18-
/* @Constructor: CxxMirror
19-
@params: 'const std::vector<Function>&'
20-
* accepts vector of 'Function' objects, which are hash-key to lookup a functor.
21-
* the only constructor to construct 'CxxMirror' object.
22-
* Syntax for constructing - CxxMirror({ type().function("func_name").build(), ..., ... })
23-
* '.build()' function will return a 'Function' object, and passed to std::vector initializer list.
24-
* the vector is simply forwarded to the base class constructor.
25-
*/ CxxMirror::CxxMirror(const std::vector<Function>& pFunctions) : detail::CxxReflection(pFunctions)
18+
CxxMirror::CxxMirror(const std::vector<Function>& pFunctions) : detail::CxxReflection(pFunctions)
2619
{
2720
rtl::detail::ReflectedConversions::init();
2821
}
2922

30-
/* @method: getRecord
31-
@param: const std::string& (name of the class/struct)
32-
@return: std::optional<Record>
33-
* if the class/struct isn't found by the given name, std::nullopt is returned.
34-
* every class/struct's is grouped under a namespace.
35-
* if no namespace is specified while registration, NAMESPACE_GLOBAL is used.
36-
*/ std::optional<Record> CxxMirror::getRecord(const std::string& pRecord) const
23+
/**
24+
* @method getRecord
25+
*
26+
* @param pRecordName The name of the class or struct to look up.
27+
* @return std::optional<rtl::Record>
28+
* Returns a valid Record if the type is found by name in default namespace group; otherwise, std::nullopt.
29+
*
30+
* All registered classes and structs are grouped under a namespace.
31+
* If no namespace is specified during registration, NAMESPACE_GLOBAL(the default) is used. */
32+
std::optional<Record> CxxMirror::getRecord(const std::string& pRecordName) const
3733
{
38-
return getRecord(std::string(detail::NAMESPACE_GLOBAL), pRecord);
34+
return getRecord(std::string(detail::NAMESPACE_GLOBAL), pRecordName);
3935
}
4036

41-
/* @method: getFunction
42-
@param: const std::string& (name of the non-member function)
43-
@return: std::optional<Function>
44-
* if the function isn't found by the given name, std::nullopt is returned.
45-
* every function is grouped under a namespace.
46-
* if no namespace is specified while registration, NAMESPACE_GLOBAL is used.
47-
*/ std::optional<Function> CxxMirror::getFunction(const std::string& pFunction) const
37+
/**
38+
* @method getFunction
39+
*
40+
* @param pFunctionName The name of the non-member function to look up.
41+
* @return std::optional<rtl::Function>
42+
* Returns a valid Function if found by name in default namespace group; otherwise, std::nullopt.
43+
*
44+
* All registered non-member functions are grouped under a namespace.
45+
* If no namespace is specified during registration, NAMESPACE_GLOBAL(the default) is used. */
46+
std::optional<Function> CxxMirror::getFunction(const std::string& pFunctionName) const
4847
{
49-
return getFunction(std::string(detail::NAMESPACE_GLOBAL), pFunction);
48+
return getFunction(std::string(detail::NAMESPACE_GLOBAL), pFunctionName);
5049
}
5150

52-
std::optional<Record> CxxMirror::getRecord(const std::size_t pRecordId) const
51+
/**
52+
* @method getRecord
53+
*
54+
* @param pRecordId The RTL-specific unique type identifier.
55+
* @return std::optional<rtl::Record>
56+
* Returns a valid Record if the type is found; otherwise, std::nullopt.
57+
*
58+
* Every registered type `T` is assigned a unique integer type ID, which can be
59+
* obtained via `rtl::traits::uid<T>::value` and cached for efficient lookup.
60+
*
61+
* The primary benefit of using a type ID is that it avoids the need to provide
62+
* the namespace and type name as strings during lookup. */
63+
std::optional<Record> CxxMirror::getRecord(const traits::uid_t pRecordId) const
5364
{
5465
const auto& recordMap = getRecordIdMap();
5566
const auto& itr = recordMap.find(pRecordId);
5667
return (itr == recordMap.end() ? std::nullopt : std::make_optional(itr->second));
5768
}
5869

59-
/* @method: getRecord
60-
@param: std::string (namespace name), std::string (class/struct name)
61-
@return: std::optional<Record>
62-
* retrieves the class/struct (as Record) registered under the given namespace.
63-
* if the class/struct isn't found by the given name, std::nullopt is returned.
64-
*/ std::optional<Record> CxxMirror::getRecord(const std::string& pNameSpace, const std::string& pRecord) const
70+
/**
71+
* @method getRecord
72+
*
73+
* @param pNameSpaceName The namespace under which the type was registered.
74+
* @param pRecordName The name of the type to look up.
75+
* @return std::optional<Record>
76+
* Returns a valid Record if the type is found by name in the given namespace group; otherwise, std::nullopt.
77+
*
78+
* Retrieves the class or struct registered under the specified namespace. */
79+
std::optional<Record> CxxMirror::getRecord(const std::string& pNameSpaceName, const std::string& pRecordName) const
6580
{
6681
const auto& nsRecordMap = getNamespaceRecordMap();
67-
const auto& itr = nsRecordMap.find(pNameSpace);
82+
const auto& itr = nsRecordMap.find(pNameSpaceName);
6883
if (itr != nsRecordMap.end())
6984
{
7085
const auto& recordMap = itr->second;
71-
const auto& itr0 = recordMap.find(pRecord);
86+
const auto& itr0 = recordMap.find(pRecordName);
7287
if (itr0 != recordMap.end()) {
7388
return std::make_optional(itr0->second);
7489
}
7590
}
7691
return std::nullopt;
7792
}
7893

79-
/* @method: getFunction
80-
@param: namespace name (std::string), non-mermber function name (std::string)
81-
@return: std::optional<Function>
82-
* retrieves the function (as 'Function' object) registered under the given namespace.
83-
* if the function isn't found by the given name, std::nullopt is returned.
84-
*/ std::optional<Function> CxxMirror::getFunction(const std::string& pNameSpace, const std::string& pFunction) const
94+
/**
95+
* @method getFunction
96+
*
97+
* @param pNameSpaceName The namespace under which the function was registered.
98+
* @param pFunctionName The name of the function to look up.
99+
* @return std::optional<Function>
100+
* Returns a valid Function if found by name in the given namespace group; otherwise, std::nullopt.
101+
*
102+
* Retrieves the non-member function registered under the specified namespace. */
103+
std::optional<Function> CxxMirror::getFunction(const std::string& pNameSpaceName, const std::string& pFunctionName) const
85104
{
86105
const auto& nsFunctionMap = getNamespaceFunctionsMap();
87-
const auto& itr = nsFunctionMap.find(pNameSpace);
106+
const auto& itr = nsFunctionMap.find(pNameSpaceName);
88107
if (itr != nsFunctionMap.end())
89108
{
90109
const auto& functionMap = itr->second;
91-
const auto& itr0 = functionMap.find(pFunction);
110+
const auto& itr0 = functionMap.find(pFunctionName);
92111
if (itr0 != functionMap.end()) {
93112
return std::make_optional(itr0->second);
94113
}

0 commit comments

Comments
 (0)