Skip to content

Upstream changes into the main repository - #1

Open
royvandam wants to merge 11 commits into
royvandam:masterfrom
solbjorn:master
Open

Upstream changes into the main repository#1
royvandam wants to merge 11 commits into
royvandam:masterfrom
solbjorn:master

Conversation

@royvandam

@royvandam royvandam commented Mar 2, 2026

Copy link
Copy Markdown
Owner

Hi Alexander (@solbjorn),

I came across your fork of the repository and noticed the changes you made. I think these improvements could benefit others using the project as well. I’d like to bring your contributions back into the main repository, if you’re okay with that.

I wasn’t able to contact you directly on GitHub, so I’m reaching out through this pull request. Would you be open to creating a pull request from your fork to the main repository?

Thanks again for the work you put into it.

Roy

solbjorn added 10 commits June 24, 2025 03:08
Signed-off-by: Alexander Lobakin <alobakin@mailbox.org>
This lifts up the C++ standard requirement to C++20, but I don't care
much.
Just use std::source_location::current().function_name() to get a nice
signature. It's a bit different in format between MSVC and GCC/Clang,
but both works well here for generating a stable unique type hash.

Signed-off-by: Alexander Lobakin <alobakin@mailbox.org>
Imagine the following:

* class A
* class B : A
* class C : A
* class D : B, C

We can't static_cast<A*>(D*), because we have 2 instances of A in D.
The list of all available casts is generated during the compilation,
but currently there's no check whether the cast can be performed.
Use C++20's concept to handle this.
Note that dynamic_cast<A*>(D*) would behave the same -- ambiguous
casts are not allowed.

Signed-off-by: Alexander Lobakin <alobakin@mailbox.org>
dynamic_cast<void*> is a special expression to get the most derived
object address, e.g. to free the memory.
Since ::typeId() always returns the ID of the most derived object,
we can use it when ::cast<void> is called and return the same value
what dynamic_cast<void*> would return.

Signed-off-by: Alexander Lobakin <alobakin@mailbox.org>
RTTI::Enable is not meant to be used standalone; only to be a virtual
base of concrete classes. That said, it's fully safe to mark it as
`novtable` to optimize the code a little bit more.

Signed-off-by: Alexander Lobakin <alobakin@mailbox.org>
Reuse a piece of refl-cpp[1] to be able to get a list of all parents,
including all grand and grand-grand parents etc.

Typical usage would be:

template <typename T>
using xr_sol_bases = typename RTTI::type_descriptor<T>::base_types ::template to<sol::bases>;

(replace sol::bases with the typelist you like)

and then

xr_sol_bases<Derived>

will expand to

sol::bases<Parent1, Parent2, Parent1OfParent1, Parent1OfParent2>

Obviously, only if all your classes are annotated with
RTTI_DECLARE_TYPEINFO().

refl-cpp requires you to do annotations separately and *after* the class
declaration, but in this version, it expands automatically from the
above macro.

[1] https://github.com/veselink1/refl-cpp

Signed-off-by: Alexander Lobakin <alobakin@mailbox.org>
C++ Core Guidelines say: if your class is a base with virtual methods,
it must always be non-copyable non-movable.
It's enough to just delete the move assignment operator to enforce this.

Also, provide RTTI_DECLARE_TRIVIAL() to crete hierarchies of trivial
(non-polymorphic types). You can't use dynamic casting, but you can
still get a class' TypeInfo, including ID, parents etc.

Fix `-Wextra-semi` (require `RTTI_DECLARE_*()` calls to end with a ';')
and use one more rule from CCG -- never use both `virtual` and
`override`.

Signed-off-by: Alexander Lobakin <alobakin@mailbox.org>
Explicitly narrow char to uint32_t (we don't care about the sign) and
prefer uniform initializers over macros.
Also prefer universal header guards over the pragma.

Signed-off-by: Alexander Lobakin <alobakin@mailbox.org>
Signed-off-by: Alexander Lobakin <alobakin@mailbox.org>
Signed-off-by: Alexander Lobakin <alobakin@mailbox.org>
@solbjorn

solbjorn commented Mar 2, 2026

Copy link
Copy Markdown

Hi!

I thought you abandoned this repo, thus I didn't create any pull requests here =\

Could you please recheck the commits whether everything complies with your code style / design preferences? If so, you can take everything as it is, otherwise let me know / do a review and I'll adjust the code.

When it comes to testing, it's been almost a year since I switched the whole https://github.com/solbjorn/reaper-engine to this implementation (there are hundreds of classes with complex hierarchies), including the piece from refl-cpp, and had no issues. I also have a debug option in the engine which checks the result of every downcast again dynamic_cast<>() and had no reports.

Thanks!

* use inline variable tags from Abseil instead of FNV-1a expanded
  compile-time. This reduces possible collisions to 0 (as the type
  ID is now a unique pointer) and improves compilation time, since
  the compiler doesn't need to recursively calculate hashes from
  std::source_location hacks. Also the type ID is now a native
  word (sizeof(void*)) which is faster to compare;
* instead of taking the result of dynamic cast for each parent into
  a std::array and then do std::find(), use simple folded recursion
  which expands to an if-else ladder and short-circuits when the
  first non-nullptr result is met;
* avoid double traversing the tree of parents (Is() + DynamicCast())
  now that DynamicCast() is as fast and simple as Is().

Assisted-by: Cyberdyne Systems Series 800 Model 101 Version 2.4
Signed-off-by: Alexander Lobakin <alobakin@mailbox.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants