C++ Logo

sg7

Advanced search

Re: [SG7] C++ decorators using reflection

From: David Rector <davrec_at_[hidden]>
Date: Sat, 12 Jun 2021 09:54:53 -0400
> On Jun 10, 2021, at 11:41 AM, Dvir Yitzchaki via SG7 <sg7_at_[hidden]> wrote:
>
> Hi.
>
> I’ve been playing around implementing a feature similar to python decorators using the reflection implementation in https://github.com/lock3/meta <https://github.com/lock3/meta>, based on the [P1240](https://wg21.link/P1240 <https://wg21.link/P1240>] syntax.
> Currently I’ve managed to compile the following code on my branch:
>
> #include <experimental/compiler>
> #include <experimental/meta>
> #include <iostream>
> #include <utility>
>
> #define FWD(x) std::forward<decltype(x)>(x)
>
> namespace meta = std::experimental::meta;
>
> using string_type = const char*;
>
> constexpr bool string_eq(string_type s1, string_type s2) {
> while (*s1 != '\0' && *s1 == *s2) {
> s1++;
> s2++;
> }
>
> return *s1 == *s2;
> }
>
> consteval void decorate(meta::info source) {
> for (auto mem : meta::members_of(source)) {
> if (meta::has_attribute(mem, "decorator")) {
> if (not meta::is_function(mem)) {
> meta::compiler.error("can only decorate functions");
> }
>
> for (const auto attribute : meta::attributes_of(mem)) {
> if (string_eq(meta::name_of(attribute), "decorator")) {
> for (const auto decorator : meta::attribute_arguments_of(attribute)) {
> ->fragment struct {
> template <typename... Args>
> static auto unqualid(% {mem})(Args&&... args) {
> return idexpr(% {decorator})(idexpr(% {mem}))(FWD(args)...);
> }
> };
> }
> meta::set_new_name(mem, __concatenate("orig_", meta::name_of(mem)));
> ->mem;
> }
> }
> }
> }
> }
>
> template <typename F>
> auto logger(F&& f) {
> return [f]<typename... Args>(Args && ... args) {
> std::cout << "calling " << meta::name_of(reflexpr(f));
> std::cout << "(";
> (void)(std::cout << ... << args);
> std::cout << ")\n";
> return f(FWD(args)...);
> };
> }
>
> struct(decorate) Foo{
>
> [[decorator(logger)]] static void fn(int){}
>
> };
>
> int main() {
> meta::compiler.print(reflexpr(Foo));
> Foo::fn(42);
> }
> with this output (from a local compiler explorer I run)
>
> ASM generation compiler returned: 0
> struct Foo {
> template <typename ...Args> static auto fn(Args &&...args) {
> return logger(fn)(std::forward<decltype(args)>(args)...);
> }
> static void orig_fn(int) {
> }
> };
> Execution build compiler returned: 0
> Program returned: 0
> calling f(42)
> For this to work I had to tell clang not to remove unknown attributes as well as add some attribute reflection functions to meta.
>
> Do you think this is a valuable usage?
>
> Do you think attribute reflection functions should be added to the standard meta library?
>
Absolutely. Seems very useful to be able to assign "tags" to certain declarations via attributes that are meaningless to the compiler, and depend on that information in metaprogramming. E.g. one might want to mark which methods in a class are factory methods, and handle them differently in a metaclass — unclear how to do this without using attributes in the way you’ve described.

> Best regards,
> Dvir
>
> --
> SG7 mailing list
> SG7_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/sg7


Received on 2021-06-12 08:54:58