Hacker News new | ask | show | jobs
by kris-jusiak 874 days ago
https://wg21.link/P2996 allows form of stateful meta-programming without using friend injection.

Meta-counter is pretty easy - https://godbolt.org/z/91M56a5dd as we just iterate over complete instantiations of the counter class (tricky part is to force the instantiation).

The following is a bit more complex example of stateful meta-programming - compile-time type list.

Firstly, C++20 version (based on friend injection) - works on gcc,clang,msvc

    template<class...> struct type_list {};

    namespace detail {
    template<auto> struct nth { auto friend get(nth); auto friend get(nth); };         
    template<auto N, class T> struct set { auto friend get(nth<N>) { return T{}; } }; 
    template<class T, template<class...> class TList, class... Ts> auto append(TList<Ts...>) -> TList<Ts..., T>;
    } // namespace detail

    template<class T, auto N = 0, auto unique = []{}>
    consteval auto append() {
        if constexpr (requires { get(detail::nth<N>{}); }) {
            append<T, N+1, unique>();
        } else if constexpr (N == 0) {
            void(detail::set<N, type_list<T>>{});
        } else {
            void(detail::set<N, decltype(detail::append<T>(get(detail::nth<N-1>{})))>{});
        }
    }

    template<auto unique = []{}, auto N = 0>
    consteval auto get_list() {
        if constexpr (requires { get(detail::nth<N>{}); }) {
            return get_list<unique, N+1>();
        } else if constexpr (N == 0) {
            return type_list{};
        } else {
            return get(detail::nth<N-1>{});
        }
    }

    int main() {
      static_assert(typeid(get_list()) == typeid(type_list<>));

      append<int>();
      static_assert(typeid(get_list()) == typeid(type_list<int>));

      append<float>();
      static_assert(typeid(get_list()) == typeid(type_list<int, float>));
    }
Full example -> https://godbolt.org/z/axPT88e3c

Now, C++26 version with the reflection proposal (based on injecting classes with members) - works on EDG

    template<class...> struct type_list{};

    namespace detail {
    template<auto> struct type_list;
    consteval auto append(auto new_member) {
      std::vector<std::meta::info> members{};
      for (auto i = 0;; ++i) {
        if (auto mi = substitute(^type_list, { std::meta::reflect_value(i) }); std::meta::is_incomplete_type(mi)) {
          std::vector<std::meta::nsdm_description> new_members{};
          for (auto member: members) {
            new_members.push_back({std::meta::type_of(member), {.name = std::meta::name_of(member)}});
          }
          const char name[]{'_', char(i+'0'), 0}; // there are defo better ways to do that
          new_members.push_back({{new_member}, {.name = std::string_view(name, 2)}});
          return define_class(mi, new_members);
        } else {
          members = std::meta::nonstatic_data_members_of(mi);
        }
      }
    }

    consteval auto get_list() {
      std::vector<std::meta::info> members{};
      for (auto i = 0;; ++i) {
        if (auto mi = substitute(^type_list, { std::meta::reflect_value(i) }); std::meta::is_incomplete_type(mi)) {
          break;
        } else {
          members = std::meta::nonstatic_data_members_of(mi);
        }
      }
      std::vector<std::meta::info> new_members{};
      for (auto member : members) { new_members.push_back(std::meta::type_of(member)); }
      return substitute(^::type_list, new_members);
    }
    } // namespace detail

    template<class T> using append = [:detail::append(^T):];
    template<auto = []{}> using get_list = [:detail::get_list():];

    int main() {
      static_assert(typeid(get_list<>) == typeid(type_list<>));

      append<int>();
      static_assert(typeid(get_list<>) == typeid(type_list<int>));

      append<float>();
      static_assert(typeid(get_list<>) == typeid(type_list<int, float>));
    }
Full example -> https://godbolt.org/z/5s8YvYqqr