struct_reflection.h 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #ifndef CARBON_COMMON_STRUCT_REFLECTION_H_
  5. #define CARBON_COMMON_STRUCT_REFLECTION_H_
  6. // Reflection support for simple struct types.
  7. //
  8. // Example usage:
  9. //
  10. // ```
  11. // struct A { int x; std::string y; };
  12. //
  13. // A a;
  14. // std::tuple<int, std::string> t = StructReflection::AsTuple(a);
  15. // ```
  16. //
  17. // Limitations:
  18. //
  19. // - Only simple aggregate structs are supported. Types with base classes,
  20. // non-public data members, constructors, or virtual functions are not
  21. // supported.
  22. // - Structs with more than 6 fields are not supported. This limit is easy to
  23. // increase if needed, but removing it entirely is hard.
  24. // - Structs containing a reference to the same type are not supported.
  25. #include <tuple>
  26. #include <type_traits>
  27. namespace Carbon::StructReflection {
  28. namespace Internal {
  29. // A type that can be converted to any field type within type T.
  30. template <typename T>
  31. struct AnyField {
  32. template <typename FieldT>
  33. // NOLINTNEXTLINE(google-explicit-constructor)
  34. operator FieldT&() const;
  35. template <typename FieldT>
  36. // NOLINTNEXTLINE(google-explicit-constructor)
  37. operator FieldT&&() const;
  38. // Don't allow conversion to T itself. This ensures we don't match against a
  39. // copy or move constructor.
  40. operator T&() const = delete;
  41. operator T&&() const = delete;
  42. };
  43. // The detection mechanism below intentionally misses field initializers.
  44. #pragma clang diagnostic push
  45. #pragma clang diagnostic ignored "-Wmissing-field-initializers"
  46. // Detector for whether we can list-initialize T from the given list of fields.
  47. template <typename T, typename... Fields>
  48. constexpr auto CanListInitialize(decltype(T{Fields()...})* /*unused*/) -> bool {
  49. return true;
  50. }
  51. template <typename T, typename... Fields>
  52. constexpr auto CanListInitialize(...) -> bool {
  53. return false;
  54. }
  55. #pragma clang diagnostic pop
  56. // Simple detector to find the number of data fields in a struct. This proceeds
  57. // in two passes:
  58. //
  59. // 1) Add AnyField<T>s until we can initialize T from our list of initializers.
  60. // 2) Add more AnyField<T>s until we can't initialize any more.
  61. template <typename T, bool AnyWorkedSoFar = false, typename... Fields>
  62. constexpr auto CountFields() -> int {
  63. if constexpr (CanListInitialize<T, Fields...>(0)) {
  64. return CountFields<T, true, Fields..., AnyField<T>>();
  65. } else if constexpr (AnyWorkedSoFar) {
  66. constexpr int NumFields = sizeof...(Fields) - 1;
  67. static_assert(NumFields <= 6, "Unsupported: too many fields in struct");
  68. return NumFields;
  69. } else if constexpr (sizeof...(Fields) > 32) {
  70. // If we go too far without finding a working initializer, something
  71. // probably went wrong with our calculation. Bail out before we recurse too
  72. // deeply.
  73. static_assert(sizeof...(Fields) <= 32,
  74. "Internal error, could not count fields in struct");
  75. } else {
  76. return CountFields<T, false, Fields..., AnyField<T>>();
  77. }
  78. }
  79. // Utility to access fields by index.
  80. template <int NumFields>
  81. struct FieldAccessor;
  82. template <>
  83. struct FieldAccessor<0> {
  84. template <typename T>
  85. static auto Get(T& /*value*/) -> auto {
  86. return std::tuple<>();
  87. }
  88. };
  89. template <>
  90. struct FieldAccessor<1> {
  91. template <typename T>
  92. static auto Get(T& value) -> auto {
  93. auto& [field0] = value;
  94. return std::tuple<decltype(field0)>(field0);
  95. }
  96. };
  97. template <>
  98. struct FieldAccessor<2> {
  99. template <typename T>
  100. static auto Get(T& value) -> auto {
  101. auto& [field0, field1] = value;
  102. return std::tuple<decltype(field0), decltype(field1)>(field0, field1);
  103. }
  104. };
  105. template <>
  106. struct FieldAccessor<3> {
  107. template <typename T>
  108. static auto Get(T& value) -> auto {
  109. auto& [field0, field1, field2] = value;
  110. return std::tuple<decltype(field0), decltype(field1), decltype(field2)>(
  111. field0, field1, field2);
  112. }
  113. };
  114. template <>
  115. struct FieldAccessor<4> {
  116. template <typename T>
  117. static auto Get(T& value) -> auto {
  118. auto& [field0, field1, field2, field3] = value;
  119. return std::tuple<decltype(field0), decltype(field1), decltype(field2),
  120. decltype(field3)>(field0, field1, field2, field3);
  121. }
  122. };
  123. template <>
  124. struct FieldAccessor<5> {
  125. template <typename T>
  126. static auto Get(T& value) -> auto {
  127. auto& [field0, field1, field2, field3, field4] = value;
  128. return std::tuple<decltype(field0), decltype(field1), decltype(field2),
  129. decltype(field3), decltype(field4)>(
  130. field0, field1, field2, field3, field4);
  131. }
  132. };
  133. template <>
  134. struct FieldAccessor<6> {
  135. template <typename T>
  136. static auto Get(T& value) -> auto {
  137. auto& [field0, field1, field2, field3, field4, field5] = value;
  138. return std::tuple<decltype(field0), decltype(field1), decltype(field2),
  139. decltype(field3), decltype(field4), decltype(field5)>(
  140. field0, field1, field2, field3, field4, field5);
  141. }
  142. };
  143. } // namespace Internal
  144. // Get the fields of the struct `T` as a tuple.
  145. template <typename T>
  146. auto AsTuple(T value) -> auto {
  147. // We use aggregate initialization to detect the number of fields.
  148. static_assert(std::is_aggregate_v<T>, "Only aggregates are supported");
  149. return Internal::FieldAccessor<Internal::CountFields<T>()>::Get(value);
  150. }
  151. } // namespace Carbon::StructReflection
  152. #endif // CARBON_COMMON_STRUCT_REFLECTION_H_