Date: Mon, 16 Oct 2023 10:36:33 +0300
Corentin Jabot wrote:
> You might be interested in https://wg21.link/p1887 which is an old paper of
> mine on that topic. I'm not sure it aged well though.
>
> I do agree that reflection on attribute would be useful but it's a surprisingly
> open design space.
> We probably want a slightly different syntax because user defined attributes
> should not be ignorable, they should be declared, have arguments of defined
> types and so forth.
>
> Then there is the question of whether they can act a bit like decorators.
I've given attributes some thought, and my present thinking is that we want
something very much along the lines of P1887, except without the [[decorator]].
Any constant expression of user-defined type can serve as a reflected attribute.
namespace my_engine
{
struct version
{
int v;
};
// or
// enum class version: int {};
} // namespace my_engine
constexpr my_engine::version declare_version( int x )
{
return version{ x };
}
struct A
{
[[+my_engine::version(4)]] int x;
[[+declare_version(4)]] int x;
}
However, the main problem with this is that attributes are currently ignorable,
and must remain so. Some implementations such as Clang don't store unknown
attributes in the AST at all, so it's not possible to get to them via reflection.
So, what we want here is not an attribute, but a new thing, I call it an annotation;
we only need to come up with the syntax for it.
It's a bit annoying that the new thing behaves grammatically, syntactically and
(mostly) semantically in the exact same way as an attribute, but isn't. But such
is life.
Some syntax suggestions:
[[+version(4)]]
[[[version(4)]]]
[[annotate(version(4))]]
annotate(version(4))
The problem with reusing the [[]] notation is that at present, even C compilers
can parse the attribute grammar and ignore it, but the first two suggestions
above do not conform to it. Third one does, though.
Then again, contracts don't either, so it's not clear how much this argument
will hold water in C++26 and beyond. We might also have the contract-like option
[[annotate: version(4)]]
> So because all of this needs a more throughout exploration it seems
> reasonable to me to not propose them as part of an initial effort. But I do hope
> we end up there.
>
>
>
>
>
> On Mon, Oct 16, 2023, 04:59 Jeremy Ong via SG7 <sg7_at_[hidden]
> <mailto:sg7_at_[hidden]> > wrote:
>
>
> First, a hearty thanks to the authors of P2996R0 (https://www.open-
> std.org/jtc1/sc22/wg21/docs/papers/2023/p2996r0.html).
>
> One item that appeared to be absent from the discussion is the notion of
> user-defined attributes. My feedback largely stems from the games industry,
> where virtually every game and game engine employs reflection to some
> degree, but I suspect that these use cases generalize well. To be clear, the
> intent isn't to wave my hands and create work, but hopefully to offer a
> different perspective regarding how custom C++ serialization is used in the
> games industry today.
>
> Given a data struct, this type of pattern will be seen in many game
> engines (fairly contrived pseudocode):
>
> ```
> class PlayerState
> {
> public:
> // This is a member function that is exposed to gameplay scripts
> automatically (e.g. Lua)
> [[scriptable]]
> void teleport(float3 position);
>
> // This is a property that is serialized when the player is serialized.
> // In addition, when the property changes, the changes are replicated
> across the network
> // for multiplayer games.
> [[serializable]]
> [[client_server]]
> int health;
>
> // ...
> };
>
> ```
>
> The functionality above is generally provided by some sort of compiler
> extension or (more commonly) a custom parser/preprocessing step. The idea
> is to embed additional metadata about member variables, functions, and types
> that are used across the build and tooling ecosystem. Other examples of such
> attributes that I've seen:
>
> - A "client only" or "server only" attribute used to control how and
> where data is replicated for multiplayer games
> - An "editor function" attribute used to indicate that a function should
> be bound to the engine's scripting context
> - A "gpu constant" attribute used to inform the build system to validate
> that the member alignments match the layout expected by the GPU
> - A "description" attribute used to populate tooltips and help text for a
> given enum field, member variable, or function.
> A "quantization" attribute used to indicate that on serialization, the
> value may be compressed/packed.
>
> These are just a few examples among hundreds of attributes that I have
> encountered across multiple large-scale game engines at multiple companies.
> Notice also that some attributes given in the last couple of examples are
> parameterized to accept a value as well (e.g. [[description = "some helpful
> text"]]).
>
> To give more concrete examples, here are example attributes supported
> by Unreal Engine (which they refer to as "specifiers"):
>
> - Class attributes: https://docs.unrealengine.com/4.27/en-
> US/ProgrammingAndScripting/GameplayArchitecture/Classes/Specifiers/
> - Function attributes: https://docs.unrealengine.com/4.27/en-
> US/ProgrammingAndScripting/GameplayArchitecture/Functions/Specifiers/
> - Property attributes: https://docs.unrealengine.com/4.27/en-
> US/ProgrammingAndScripting/GameplayArchitecture/Properties/Specifiers/
> - Struct attributes: https://docs.unrealengine.com/4.27/en-
> US/ProgrammingAndScripting/GameplayArchitecture/Structs/Specifiers/
>
> Such systems are sufficiently mature that I don't think anyone could
> reasonably expect the first real incarnation of C++ reflection to cover all
> existing use-cases, but perhaps such examples in the wild may be useful in
> motivating additional feature coverage. I think even just non-parameterized
> attributes supported by the splice and meta concepts introduced in the
> reflection proposal would get significant mileage and go a long way towards
> eventually deprecating existing custom pre-compiler frontends.
>
> Thanks for reading,
> Jeremy
> --
> SG7 mailing list
> SG7_at_[hidden] <mailto:SG7_at_[hidden]>
> https://lists.isocpp.org/mailman/listinfo.cgi/sg7
>
> You might be interested in https://wg21.link/p1887 which is an old paper of
> mine on that topic. I'm not sure it aged well though.
>
> I do agree that reflection on attribute would be useful but it's a surprisingly
> open design space.
> We probably want a slightly different syntax because user defined attributes
> should not be ignorable, they should be declared, have arguments of defined
> types and so forth.
>
> Then there is the question of whether they can act a bit like decorators.
I've given attributes some thought, and my present thinking is that we want
something very much along the lines of P1887, except without the [[decorator]].
Any constant expression of user-defined type can serve as a reflected attribute.
namespace my_engine
{
struct version
{
int v;
};
// or
// enum class version: int {};
} // namespace my_engine
constexpr my_engine::version declare_version( int x )
{
return version{ x };
}
struct A
{
[[+my_engine::version(4)]] int x;
[[+declare_version(4)]] int x;
}
However, the main problem with this is that attributes are currently ignorable,
and must remain so. Some implementations such as Clang don't store unknown
attributes in the AST at all, so it's not possible to get to them via reflection.
So, what we want here is not an attribute, but a new thing, I call it an annotation;
we only need to come up with the syntax for it.
It's a bit annoying that the new thing behaves grammatically, syntactically and
(mostly) semantically in the exact same way as an attribute, but isn't. But such
is life.
Some syntax suggestions:
[[+version(4)]]
[[[version(4)]]]
[[annotate(version(4))]]
annotate(version(4))
The problem with reusing the [[]] notation is that at present, even C compilers
can parse the attribute grammar and ignore it, but the first two suggestions
above do not conform to it. Third one does, though.
Then again, contracts don't either, so it's not clear how much this argument
will hold water in C++26 and beyond. We might also have the contract-like option
[[annotate: version(4)]]
> So because all of this needs a more throughout exploration it seems
> reasonable to me to not propose them as part of an initial effort. But I do hope
> we end up there.
>
>
>
>
>
> On Mon, Oct 16, 2023, 04:59 Jeremy Ong via SG7 <sg7_at_[hidden]
> <mailto:sg7_at_[hidden]> > wrote:
>
>
> First, a hearty thanks to the authors of P2996R0 (https://www.open-
> std.org/jtc1/sc22/wg21/docs/papers/2023/p2996r0.html).
>
> One item that appeared to be absent from the discussion is the notion of
> user-defined attributes. My feedback largely stems from the games industry,
> where virtually every game and game engine employs reflection to some
> degree, but I suspect that these use cases generalize well. To be clear, the
> intent isn't to wave my hands and create work, but hopefully to offer a
> different perspective regarding how custom C++ serialization is used in the
> games industry today.
>
> Given a data struct, this type of pattern will be seen in many game
> engines (fairly contrived pseudocode):
>
> ```
> class PlayerState
> {
> public:
> // This is a member function that is exposed to gameplay scripts
> automatically (e.g. Lua)
> [[scriptable]]
> void teleport(float3 position);
>
> // This is a property that is serialized when the player is serialized.
> // In addition, when the property changes, the changes are replicated
> across the network
> // for multiplayer games.
> [[serializable]]
> [[client_server]]
> int health;
>
> // ...
> };
>
> ```
>
> The functionality above is generally provided by some sort of compiler
> extension or (more commonly) a custom parser/preprocessing step. The idea
> is to embed additional metadata about member variables, functions, and types
> that are used across the build and tooling ecosystem. Other examples of such
> attributes that I've seen:
>
> - A "client only" or "server only" attribute used to control how and
> where data is replicated for multiplayer games
> - An "editor function" attribute used to indicate that a function should
> be bound to the engine's scripting context
> - A "gpu constant" attribute used to inform the build system to validate
> that the member alignments match the layout expected by the GPU
> - A "description" attribute used to populate tooltips and help text for a
> given enum field, member variable, or function.
> A "quantization" attribute used to indicate that on serialization, the
> value may be compressed/packed.
>
> These are just a few examples among hundreds of attributes that I have
> encountered across multiple large-scale game engines at multiple companies.
> Notice also that some attributes given in the last couple of examples are
> parameterized to accept a value as well (e.g. [[description = "some helpful
> text"]]).
>
> To give more concrete examples, here are example attributes supported
> by Unreal Engine (which they refer to as "specifiers"):
>
> - Class attributes: https://docs.unrealengine.com/4.27/en-
> US/ProgrammingAndScripting/GameplayArchitecture/Classes/Specifiers/
> - Function attributes: https://docs.unrealengine.com/4.27/en-
> US/ProgrammingAndScripting/GameplayArchitecture/Functions/Specifiers/
> - Property attributes: https://docs.unrealengine.com/4.27/en-
> US/ProgrammingAndScripting/GameplayArchitecture/Properties/Specifiers/
> - Struct attributes: https://docs.unrealengine.com/4.27/en-
> US/ProgrammingAndScripting/GameplayArchitecture/Structs/Specifiers/
>
> Such systems are sufficiently mature that I don't think anyone could
> reasonably expect the first real incarnation of C++ reflection to cover all
> existing use-cases, but perhaps such examples in the wild may be useful in
> motivating additional feature coverage. I think even just non-parameterized
> attributes supported by the splice and meta concepts introduced in the
> reflection proposal would get significant mileage and go a long way towards
> eventually deprecating existing custom pre-compiler frontends.
>
> Thanks for reading,
> Jeremy
> --
> SG7 mailing list
> SG7_at_[hidden] <mailto:SG7_at_[hidden]>
> https://lists.isocpp.org/mailman/listinfo.cgi/sg7
>
Received on 2023-10-16 07:36:36