struct_reflection.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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. // Note: Compare against the maximum number of fields supported *PLUS 1*.
  67. static_assert(sizeof...(Fields) <= 7,
  68. "Unsupported: too many fields in struct");
  69. return sizeof...(Fields) - 1;
  70. } else if constexpr (sizeof...(Fields) > 32) {
  71. // If we go too far without finding a working initializer, something
  72. // probably went wrong with our calculation. Bail out before we recurse too
  73. // deeply.
  74. static_assert(sizeof...(Fields) <= 32,
  75. "Internal error, could not count fields in struct");
  76. } else {
  77. return CountFields<T, false, Fields..., AnyField<T>>();
  78. }
  79. }
  80. // Utility to access fields by index.
  81. template <int NumFields>
  82. struct FieldAccessor;
  83. template <>
  84. struct FieldAccessor<0> {
  85. template <typename T>
  86. static auto Get(T& /*value*/) -> auto {
  87. return std::tuple<>();
  88. }
  89. };
  90. template <>
  91. struct FieldAccessor<1> {
  92. template <typename T>
  93. static auto Get(T& value) -> auto {
  94. auto& [field0] = value;
  95. return std::tuple<decltype(field0)>(field0);
  96. }
  97. };
  98. template <>
  99. struct FieldAccessor<2> {
  100. template <typename T>
  101. static auto Get(T& value) -> auto {
  102. auto& [field0, field1] = value;
  103. return std::tuple<decltype(field0), decltype(field1)>(field0, field1);
  104. }
  105. };
  106. template <>
  107. struct FieldAccessor<3> {
  108. template <typename T>
  109. static auto Get(T& value) -> auto {
  110. auto& [field0, field1, field2] = value;
  111. return std::tuple<decltype(field0), decltype(field1), decltype(field2)>(
  112. field0, field1, field2);
  113. }
  114. };
  115. template <>
  116. struct FieldAccessor<4> {
  117. template <typename T>
  118. static auto Get(T& value) -> auto {
  119. auto& [field0, field1, field2, field3] = value;
  120. return std::tuple<decltype(field0), decltype(field1), decltype(field2),
  121. decltype(field3)>(field0, field1, field2, field3);
  122. }
  123. };
  124. template <>
  125. struct FieldAccessor<5> {
  126. template <typename T>
  127. static auto Get(T& value) -> auto {
  128. auto& [field0, field1, field2, field3, field4] = value;
  129. return std::tuple<decltype(field0), decltype(field1), decltype(field2),
  130. decltype(field3), decltype(field4)>(
  131. field0, field1, field2, field3, field4);
  132. }
  133. };
  134. template <>
  135. struct FieldAccessor<6> {
  136. template <typename T>
  137. static auto Get(T& value) -> auto {
  138. auto& [field0, field1, field2, field3, field4, field5] = value;
  139. return std::tuple<decltype(field0), decltype(field1), decltype(field2),
  140. decltype(field3), decltype(field4), decltype(field5)>(
  141. field0, field1, field2, field3, field4, field5);
  142. }
  143. };
  144. } // namespace Internal
  145. // Get the fields of the struct `T` as a tuple.
  146. template <typename T>
  147. auto AsTuple(T value) -> auto {
  148. // We use aggregate initialization to detect the number of fields.
  149. static_assert(std::is_aggregate_v<T>, "Only aggregates are supported");
  150. return Internal::FieldAccessor<Internal::CountFields<T>()>::Get(value);
  151. }
  152. } // namespace Carbon::StructReflection
  153. #endif // CARBON_COMMON_STRUCT_REFLECTION_H_