struct_reflection.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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 5 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. operator FieldT&() const;
  34. template <typename FieldT>
  35. operator FieldT&&() const;
  36. // Don't allow conversion to T itself. This ensures we don't match against a
  37. // copy or move constructor.
  38. operator T&() const = delete;
  39. operator T&&() const = delete;
  40. };
  41. // The detection mechanism below intentionally misses field initializers.
  42. #pragma clang diagnostic push
  43. #pragma clang diagnostic ignored "-Wmissing-field-initializers"
  44. // Detector for whether we can list-initialize T from the given list of fields.
  45. template <typename T, typename... Fields>
  46. constexpr bool CanListInitialize(decltype(T{Fields()...})*) {
  47. return true;
  48. }
  49. template <typename T, typename... Fields>
  50. constexpr bool CanListInitialize(...) {
  51. return false;
  52. }
  53. #pragma clang diagnostic pop
  54. // Simple detector to find the number of data fields in a struct. This proceeds
  55. // in two passes:
  56. //
  57. // 1) Add AnyField<T>s until we can initialize T from our list of initializers.
  58. // 2) Add more AnyField<T>s until we can't initialize any more.
  59. template <typename T, bool AnyWorkedSoFar = false, typename... Fields>
  60. constexpr auto CountFields() -> int {
  61. if constexpr (CanListInitialize<T, Fields...>(0)) {
  62. return CountFields<T, true, Fields..., AnyField<T>>();
  63. } else if constexpr (AnyWorkedSoFar) {
  64. static_assert(sizeof...(Fields) <= 5,
  65. "Unsupported: too many fields in struct");
  66. return sizeof...(Fields) - 1;
  67. } else if constexpr (sizeof...(Fields) > 32) {
  68. // If we go too far without finding a working initializer, something
  69. // probably went wrong with our calculation. Bail out before we recurse too
  70. // deeply.
  71. static_assert(sizeof...(Fields) <= 32,
  72. "Internal error, could not count fields in struct");
  73. } else {
  74. return CountFields<T, false, Fields..., AnyField<T>>();
  75. }
  76. }
  77. // Utility to access fields by index.
  78. template <int NumFields>
  79. struct FieldAccessor;
  80. template <>
  81. struct FieldAccessor<0> {
  82. template <typename T>
  83. static auto Get(T& /*value*/) -> auto {
  84. return std::tuple<>();
  85. }
  86. };
  87. template <>
  88. struct FieldAccessor<1> {
  89. template <typename T>
  90. static auto Get(T& value) -> auto {
  91. auto& [field0] = value;
  92. return std::tuple<decltype(field0)>(field0);
  93. }
  94. };
  95. template <>
  96. struct FieldAccessor<2> {
  97. template <typename T>
  98. static auto Get(T& value) -> auto {
  99. auto& [field0, field1] = value;
  100. return std::tuple<decltype(field0), decltype(field1)>(field0, field1);
  101. }
  102. };
  103. template <>
  104. struct FieldAccessor<3> {
  105. template <typename T>
  106. static auto Get(T& value) -> auto {
  107. auto& [field0, field1, field2] = value;
  108. return std::tuple<decltype(field0), decltype(field1), decltype(field2)>(
  109. field0, field1, field2);
  110. }
  111. };
  112. template <>
  113. struct FieldAccessor<4> {
  114. template <typename T>
  115. static auto Get(T& value) -> auto {
  116. auto& [field0, field1, field2, field3] = value;
  117. return std::tuple<decltype(field0), decltype(field1), decltype(field2),
  118. decltype(field3)>(field0, field1, field2, field3);
  119. }
  120. };
  121. template <>
  122. struct FieldAccessor<5> {
  123. template <typename T>
  124. static auto Get(T& value) -> auto {
  125. auto& [field0, field1, field2, field3, field4] = value;
  126. return std::tuple<decltype(field0), decltype(field1), decltype(field2),
  127. decltype(field3), decltype(field4)>(
  128. field0, field1, field2, field3, field4);
  129. }
  130. };
  131. } // namespace Internal
  132. // Get the fields of the struct `T` as a tuple.
  133. template <typename T>
  134. auto AsTuple(T value) -> auto {
  135. // We use aggregate initialization to detect the number of fields.
  136. static_assert(std::is_aggregate_v<T>, "Only aggregates are supported");
  137. return Internal::FieldAccessor<Internal::CountFields<T>()>::Get(value);
  138. }
  139. } // namespace Carbon::StructReflection
  140. #endif // CARBON_COMMON_STRUCT_REFLECTION_H_