C++26 & reflexpr : Réflexion Compile-Time, std::meta

C++26 & reflexpr : Réflexion Compile-Time, std::meta

Amine Abidi - Lead Software Engineer C++/Qt - Co-fondateur PointerLab

Publié par Amine Abidi - Lead Software Engineer C++/Qt - Co-fondateur PointerLab

La Réflexion Compile-Time en C++ 26 : Le Plus Grand Tournant depuis C++11

Introduction

La réflexion à la compilation (compile-time reflection) fera officiellement partie du standard C++26. Cette avancée majeure permet au code C++ de s'inspecter lui-même à la compilation, apportant des capacités d'introspection C++ natives. Herb Sutter a décrit cette fonctionnalité comme un "whole new language", à la hauteur de l'arrivée de constexpr dans C++11.

Pour mieux comprendre les fondements de cette évolution, il peut être utile de relire certaines notions comme la gestion mémoire avec std::vector ou les pointeurs intelligents en C++, qui s’intègrent naturellement avec la nouvelle métaprogrammation C++.

Qu'est-ce que la réflexion à la compilation en C++ ?

La réflexion C++ moderne permet :

• L'inspection des types, membres, fonctions et annotations.

• La génération de code C++ automatique à la compilation.

• Un métaprogrammation compile-time (compile-time metaprogramming) expressive, sans macros.

Tout cela est rendu possible grâce au nouvel opérateur reflexpr et au namespace std::meta introduits par P2996R13.

Fonctionnalités incluses dans C++26

Parmi les papiers adoptés :

P2996R13 : base de la réflexion.

P3394R4 : gestion des annotations.

P1306R5 : boucles de réflexion (expansion statements).

P3560R2 : gestion des erreurs dans les métaprogrammes.

Cette base permet d'explorer des cas d'usage avancés dans tous les secteurs : automatisation, génération de bindings, introspection C++, etc. Pour les plus curieux, une lecture directe du papier P2996R13 sur le site de la norme ISO C++ est recommandée.

Exemple simple : introspection avec reflexpr

struct Point { double x, y, z; };


template<typename T>
void print_members() {
  for (auto member : reflexpr(T).members()) {
    std::cout << meta::get_name(member)
              << " : " << meta::get_type(member).name() << "\n";
  }
}

Ce code permet d'énumérer les membres d'une struct automatiquement.

Autres exemples concrets d’utilisation de reflexpr et std::meta

1. Inspecter les types de retour de fonctions

#include <meta>
#include <iostream>

struct Person {
  std::string name;
  int age;

  std::string get_name() const { return name; }
  int get_age() const { return age; }
};

template<typename T>
void print_function_return_types() {
  for (auto func : reflexpr(T).functions()) {
    std::cout << meta::get_name(func) << " returns "
              << meta::get_type(meta::get_return_type(func)).name() << "\n";
  }
}

int main() {
  print_function_return_types<Person>();
}

2. Générer un sérialiseur JSON automatiquement (prototype)

#include <meta>
#include <iostream>
#include <iostream>

struct Product {
  int id;
  std::string name;
  double price;
};

template<typename T>
void to_json(const T& obj) {
  std::cout << "{ ";
  bool first = true;
  for (auto member : reflexpr(T).members()) {
    if (!first) std::cout << ", ";
    first = false;
    std::cout << "\"" << meta::get_name(member) << "\": "
              << "\"" << obj.*meta::get_pointer(member) << "\"";
  }
  std::cout << " }" << std::endl;
}

int main() {
  Product p{42, "Laptop", 999.99};
  to_json(p);
}

3. Annotations pour documentation automatique

#include <meta>
#include <iostream>

struct [[meta::annotate("DTO pour API REST")]] User {
  [[meta::annotate("Identifiant unique")]]
  int id;

  [[meta::annotate("Nom complet")]]
  std::string name;
};

template<typename T>
void print_annotations() {
  std::cout << "Structure : " << meta::get_name(reflexpr(T)) << "\n";
  for (auto member : reflexpr(T).members()) {
    std::cout << "- " << meta::get_name(member) << " : ";
    for (auto ann : meta::get_annotations(member)) {
      std::cout << meta::get_value(ann) << " ";
    }
    std::cout << "\n";
  }
}

int main() {
  print_annotations<User>();
}

Cas d'usage concrets : tutoriels pratiques

Comment générer un binder Python automatiquement

Avec std::meta, il est possible de parcourir tous les champs d'une classe C++ et de générer du code compatible Python (via pybind11 par exemple).

3 erreurs fréquentes à éviter :

  1. Utiliser la réflexion avec des types non visibles (non exportés).

  2. Ignorer la gestion des erreurs lors de la génération dynamique.

  3. Mal structurer les annotations, rendant les métaprogrammes peu lisibles.

Pourquoi la fintech adopte C++26 ?

• Besoin de code ultra-performant et introspectif.

• Automatisation des bindings Python pour les moteurs quantitatifs.

• Moins de macros, plus de type safety.

Comparaison : typeid vs reflexpr

Lorsque l'on compare les mécanismes d’introspection en C++, deux approches se distinguent : typeid pour la réflexion à l’exécution et reflexpr pour la réflexion à la compilation (introduite avec C++26).

Portabilité :

typeid est disponible depuis longtemps et fait partie du standard C++, ce qui le rend largement portable. À l’inverse, reflexpr est une nouveauté introduite avec C++26, et son support dépend encore de la version des compilateurs utilisés.

Utilisation :

typeid est limité à des scénarios simples, principalement à l’exécution, et permet surtout d'obtenir des informations sur le type dynamique d’un objet. En revanche, reflexpr permet une introspection profonde et fine du code à la compilation, comme l’accès aux membres, aux types, aux signatures de fonctions, etc.

Génération de code :

Avec typeid, il est impossible de générer du code automatiquement. reflexpr, quant à lui, ouvre la voie à la génération de code à la compilation grâce à l’utilisation de l’espace de noms std::meta.

Intégration dans les compilateurs

Voici comment le compilateur traite la réflexion à la compilation avec reflexpr, avant la génération du binaire final.

GCC 16, Clang 18 et MSVC Preview testent déjà ces fonctionnalités.

• Compiler Explorer propose des forks avec reflexpr activé une plateforme incontournable pour tester ces nouveautés.

Bonnes pratiques

• Utiliser meta::annotations pour guider la génération.

• Préférer lock_guard et unique_ptr pour la sûreté.

• Isoler les métaprogrammes dans des fichiers propres.

Quiz

  1. Que fait reflexpr ?

  2. Peut-on générer un JSON à partir d'une struct ?

  3. Quelle différence entre typeid et reflexpr ?

Conclusion

La réflexion à la compilation en C++ 26 inaugure une nouvelle ère. En renforçant la génération de code C++, la métaprogrammation compile-time et l'introspection native, C++ devient plus puissant, plus clair, plus maintenable. Il est temps de vous former et de préparer vos applications pour le futur du langage. Pour un aperçu concret de ces évolutions, les blogs spécialisés comme Herb Sutter ou Meeting C++ regorgent d'exemples et de retours de la communauté internationale.

Rejoignez la communauté C++ 🇫🇷 sur Discord !

Un espace convivial pour échanger et apprendre ensemble.