Skip to main content
GameDev.net gamedev.net
🔒 Locked

C++26 Reflection Follow-up

Started by CasualYouTuber31 May 29 at 4:37 PM 10 replies 1.3k views
Original Post
CasualYouTuber31
CasualYouTuber31

Like the OP from https://gamedev.net/forums/topic/719834-c26-reflection-in-angelscript/, I'm interested in using C++26's new reflection system alongside AngelScript. I have nothing against the way application interface registration works—it offers a lot of control over what to register and how—but it introduces a lot of boilerplate, and most users probably won't care too much about registering object types etc. in specific ways. Moreover, it is quite easy for developers to introduce errors unwittingly.

Since AngelScript itself probably shouldn't use reflection, I've started writing a separate library for it to explore how viable it is: https://github.com/CasualYT31/ReflectiveAngelScriptWrapper. So far so good, but I've only tackled C++ typename -> AngelScript typename conversion at compile time, and global property registration, for now. I'm going to be tackling C++ function declarations -> AngelScript function declarations next.

I will be working on this for the next few weeks at least, hopefully longer. If it actually develops into a useful library then I would love to see this featured on angelcode.com (once reflection becomes more widely available)! But I would say it's far too soon for that yet.

RmbRT
RmbRT

I wonder how much more bloated they are going to make the C++ standard before they're going to feel embarassed. I'm glad I have my own language I'm working on, and that for everything else, I switched to plain C.

Walk with God.
sebight
sebight

CasualYouTuber31 wrote:

Like the OP from https://gamedev.net/forums/topic/719834-c26-reflection-in-angelscript/, I'm interested in using C++26's new reflection system alongside AngelScript. I have nothing against the way application interface registration works—it offers a lot of control over what to register and how—but it introduces a lot of boilerplate, and most users probably won't care too much about registering object types etc. in specific ways. Moreover, it is quite easy for developers to introduce errors unwit...

Good job, this is interesting to see. I tried to get somewhere with visit_struct and entt::meta for reflection-based AngelScript registration, but I didn't get that far.


By the way, any idea if the C++26 reflection supports operator reflection? That's what I found to be the most annoying part for my AngelScript wrapper.

CasualYouTuber31
CasualYouTuber31

sebight wrote:

By the way, any idea if the C++26 reflection supports operator reflection? That's what I found to be the most annoying part for my AngelScript wrapper.

I'm 99% sure it can, I see a few functions related to that but I haven't gotten that far yet. Currently finishing up a very basic implementation of function declaration generation, then moving on to type registration, so I'm sure I'll look into it soon.

You can read more into reflection here (with it being so new, these are basically the only decent sources of documentation, and maybe one or two Stackoverflow threads):

GCC 16.1 release notes: https://gcc.gnu.org/gcc-16/changes.html#cxx.

Main reflection paper: https://isocpp.org/files/papers/P2996R13.html#synopsis.

Function parameter reflection paper: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3096r12.pdf.

Annotation paper: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3394r4.html#proposal.

Annotations in particular are really useful, I'm not sure it would be fully possible without them, or at least not possible in as clean a way. They trivialise problems like dealing with template AngelScript types, attaching default values to function parameters, telling my library if you want to register a type as a value or a reference type, all sorts of stuff.

E.g. how do you tell the C++ compiler that you intend to store strings in a CScriptArray? The subtype of an array is runtime information and so reflection can't ordinarily deal with it. With annotations you can just stick a compile-time struct on a CScriptArray instance and the library can reflect on its annotations to figure out which subtype you intend to store in it.

In fairness you can solve some of these problems using function parameters like the base AngelScript library does, but the whole point of this wrapper is to infer as much as possible from the C++ variables/functions/classes, and parameters to registry functions are the antithesis of that aim IMO. The dream is to just shove a class into AngelScript and it just "knows" what to do to register it with the application interface.

RmbRT wrote:

I wonder how much more bloated they are going to make the C++ standard before they're going to feel embarassed. I'm glad I have my own language I'm working on, and that for everything else, I switched to plain C.

Every day I work on this library I remember more reasons why I left this language behind 😂 But reflection is quite exciting, and I think a library like this would make me more willing to pick my other AngelScript-based game/project back up.

RmbRT
RmbRT

CasualYouTuber31 said:
But reflection is quite exciting, and I think a library like this would make me more willing to pick my other AngelScript-based game/project back up.

Reflection is a fundamentally useful feature, yeah. Especially when you're doing stuff like describing vertex buffer layouts for the GPU. If that doesn't have to be done by hand or through macro magic, that'd be very helpful. Being able to iterate the fields & types of a struct, and maybe also generating code paths at runtime, etc., are all things that can make life a lot easier in lots of situations and simultaneously boost actual program performance. The reflection-based JIT code generation part is something I still haven't solved properly for my language design.

Walk with God.
WitchLord
WitchLord

Thanks for sharing. I look forward to see how this will work out.

I'll probably wait until more compilers catch up with the c++26 standard before I start doing anything on my own. But I feel this can definitely be useful for AngelScript.


AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - <a href="http://www.angelcode.com/tower" rel
CasualYouTuber31
CasualYouTuber31

I thought I would give a quick update on this project after a month's worth of progress.

The library now supports registering:

  1. Global properties.

  2. Global functions, using any call convention you require.

    1. The generic call convention works very well thanks to the auto wrapper add on. But if you need to use asIScriptGeneric* directly, then there are still ways to generate function declaration strings semi-automatically.

    2. Variable parameter types are detected using a combination of void* and int.

    3. You will still need to handwrite function declarations for variadic arguments and template functions, but these are written with/near the C++ function so it helps you keep track of what your C++ function is supposed to receive and return.

  3. Funcdefs (scoped funcdefs will be coming later).

  4. Basic reference types! This includes:

    1. Most combinations of Factory, AddRef, and Release behaviours.

    2. Public methods.

    3. Public fields/properties.

    4. Property accessors.

    5. Inheritance! Meaning we can finally register both class relationships and inherited methods & properties with a single RegisterObjectType() call!

  5. Typedefs.

  6. Enums.

  7. Interfaces.

The READMEs on the GitHub repo go into more detail if you are interested.

My next goals are to support lesser-used reference type features such as scoped ref types, weak ref behaviours, and garbage collection behaviours. Then I would like to prioritise operator registration. I will probably think about ways to support list factory functions and AngelScript template types before moving onto value types. The ultimate goal is to be able to register a namespace and everything within it, but that might be more complicated than I'd like.

In any case, that covers most of the registration functionality I'd like to implement! After that, I will think about ways to make calling script functions a lot easier (I already have some code for this in another project I worked on a few years ago, I'll just port it over and test it properly).

WitchLord
WitchLord

Nice work. I'm happy to see this progress so well.

I was really hoping to see how to detect the various flags for registering value types. I think that is probably the part that most people have dificulty with, thus it is the part that I'm the most interested in automating.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - <a href="http://www.angelcode.com/tower" rel
CasualYouTuber31
CasualYouTuber31

Final update on this reflection-based library for the time being.

I've largely completed registration features now! There are still some things that are not supported, namely:

  1. List factories and constructors.

  2. Scoped reference types.

  3. Operator registration (at least, direct mappings from C++ operators -> AngelScript operators where possible. You can still register them manually using ordinary C++ static functions/methods).

  4. Composite properties and methods.

  5. Registering template types.

But this is as far as I want to take the library for now. At some point in the near future, I want to begin using this library in another project of mine (to test it in real-world scenarios beyond those found in unit tests), though I am putting that on hold whilst I work on other things.

Before I temporarily sign off on this project, though, I've got a couple of questions that I'm hoping you have some time to answer?

  1. What is the difference between const_weakref<T> and weakref<const T>? The latter seems to work fine for me (and is a lot easier to handle in my library) so I was curious as to why the former exists.

  2. I just want to make sure that asOBJ_APP_CLASS_ALLINTS and asOBJ_APP_CLASS_ALLFLOATS don't apply recursively? E.g. if you had:

    struct ValueType {
        struct InnerType {
            int number;
        } member;
    };

    then asOBJ_APP_CLASS_ALLINTS won't apply to ValueType, correct? I'm asking because I noticed that CDateTime was not registered with such a flag, even though it only contains a time_point that (unless I'm wrong) only contains a 64-bit integer.

Thanks for taking an interest in this library! Once it's undergone more rigorous testing/use, I'm sure it will be really useful!

WitchLord
WitchLord

CasualYouTuber31 wrote:

What is the difference between const_weakref and weakref?

The difference is basically how constness is enforced.

CasualYouTuber31 wrote:

I just want to make sure that asOBJ_APP_CLASS_ALLINTS and asOBJ_APP_CLASS_ALLFLOATS don't apply recursively?

Yes. At least until now I haven't come across any ABI that would interpret struct { struct { int; int;}; }; as all integers. If I do I'll probably introduce a new flag to indicate that.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - <a href="http://www.angelcode.com/tower" rel
ZXShady
ZXShady

I am making a library that compiles for C++20 and it offers a fairly simple interface

struct Range {
    double min{};
    double max{};

    explicit operator Range() const noexcept;

    bool inside(double value) const noexcept;

    double span() const;

    double clamp(double value) const noexcept;

    double normalize(double value) const;

    double denormalize(double t) const;
};

ValueClass(engine, "Range", AngelScript::asOBJ_APP_CLASS_MORE_CONSTRUCTORS)
    .constructor<double, double>(Names{"min", "max"})
    .method<&Range::inside,
            &Range::span,
            &Range::normalize,
            &Range::denormalize,
            &Range::clamp>()
    .member<&Range::min,
            &Range::max>();

// since it is a simple aggregate it can be done with less typing

AggregateValueClass<Range>(engine, "Range") // registers the default constructor and members automaticly
    .method<&Range::inside,
            &Range::span,
            &Range::normalize,
            &Range::denormalize,
            &Range::clamp>();

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.