C++ Logo

sg7

Advanced search

[SG7] Fwd: C++ decorators using reflection

From: Dvir Yitzchaki <dvirtz_at_[hidden]>
Date: Thu, 10 Jun 2021 18:41:43 +0300
Hi.

I’ve been playing around implementing a feature similar to python
decorators using the reflection implementation in
https://github.com/lock3/meta, based on the [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?

Best regards,
Dvir

Received on 2021-06-10 10:41:59