mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 21:10:12 +01:00
Just spotted these while looking at warnings that seem to fire on our code are probably are things we'd fix if we saw them. None of these seem important FWIW. Also removes a redundant flag that is part of `-Wall`. I have a follow-up for the high-value warning I spotted that motivated me to look at all of this. But it's noisy so kept it as a separate PR.
202 lines
6.3 KiB
C++
202 lines
6.3 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#ifndef CARBON_COMMON_STRUCT_REFLECTION_H_
|
|
#define CARBON_COMMON_STRUCT_REFLECTION_H_
|
|
|
|
// Reflection support for simple struct types.
|
|
//
|
|
// Example usage:
|
|
//
|
|
// ```
|
|
// struct A { int x; std::string y; };
|
|
//
|
|
// A a;
|
|
// std::tuple<int, std::string> t = StructReflection::AsTuple(a);
|
|
// ```
|
|
//
|
|
// Limitations:
|
|
//
|
|
// - Only simple aggregate structs are supported. Types with base classes,
|
|
// non-public data members, constructors, or virtual functions are not
|
|
// supported.
|
|
// - Structs with more than 8 fields are not supported. This limit is easy to
|
|
// increase if needed, but removing it entirely is hard.
|
|
// - Structs containing a reference to the same type are not supported.
|
|
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
|
|
namespace Carbon::StructReflection {
|
|
|
|
namespace Internal {
|
|
|
|
// A type that can be converted to any field type within type T.
|
|
template <typename T>
|
|
struct AnyField {
|
|
template <typename FieldT>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
operator FieldT&() const;
|
|
|
|
template <typename FieldT>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
operator FieldT&&() const;
|
|
|
|
// Don't allow conversion to T itself. This ensures we don't match against a
|
|
// copy or move constructor.
|
|
operator T&() const = delete;
|
|
operator T&&() const = delete;
|
|
};
|
|
|
|
// The detection mechanism below intentionally misses field initializers.
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
|
|
|
// Detector for whether we can list-initialize T from the given list of fields.
|
|
template <typename T, typename... Fields>
|
|
constexpr auto CanListInitialize(decltype(T{Fields()...})* /*unused*/) -> bool {
|
|
return true;
|
|
}
|
|
template <typename T, typename... Fields>
|
|
constexpr auto CanListInitialize(...) -> bool {
|
|
return false;
|
|
}
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
// Simple detector to find the number of data fields in a struct. This proceeds
|
|
// in two passes:
|
|
//
|
|
// 1) Add AnyField<T>s until we can initialize T from our list of initializers.
|
|
// 2) Add more AnyField<T>s until we can't initialize any more.
|
|
template <typename T, bool AnyWorkedSoFar = false, typename... Fields>
|
|
constexpr auto CountFields() -> int {
|
|
if constexpr (CanListInitialize<T, Fields...>(nullptr)) {
|
|
return CountFields<T, true, Fields..., AnyField<T>>();
|
|
} else if constexpr (AnyWorkedSoFar) {
|
|
constexpr int NumFields = sizeof...(Fields) - 1;
|
|
static_assert(NumFields <= 8, "Unsupported: too many fields in struct");
|
|
return NumFields;
|
|
} else if constexpr (sizeof...(Fields) > 32) {
|
|
// If we go too far without finding a working initializer, something
|
|
// probably went wrong with our calculation. Bail out before we recurse too
|
|
// deeply.
|
|
static_assert(sizeof...(Fields) <= 32,
|
|
"Internal error, could not count fields in struct");
|
|
} else {
|
|
return CountFields<T, false, Fields..., AnyField<T>>();
|
|
}
|
|
}
|
|
|
|
// Utility to access fields by index.
|
|
template <int NumFields>
|
|
struct FieldAccessor;
|
|
|
|
template <>
|
|
struct FieldAccessor<0> {
|
|
template <typename T>
|
|
static auto Get(T& /*value*/) -> auto {
|
|
return std::tuple<>();
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct FieldAccessor<1> {
|
|
template <typename T>
|
|
static auto Get(T& value) -> auto {
|
|
auto& [field0] = value;
|
|
return std::tuple<decltype(field0)>(field0);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct FieldAccessor<2> {
|
|
template <typename T>
|
|
static auto Get(T& value) -> auto {
|
|
auto& [field0, field1] = value;
|
|
return std::tuple<decltype(field0), decltype(field1)>(field0, field1);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct FieldAccessor<3> {
|
|
template <typename T>
|
|
static auto Get(T& value) -> auto {
|
|
auto& [field0, field1, field2] = value;
|
|
return std::tuple<decltype(field0), decltype(field1), decltype(field2)>(
|
|
field0, field1, field2);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct FieldAccessor<4> {
|
|
template <typename T>
|
|
static auto Get(T& value) -> auto {
|
|
auto& [field0, field1, field2, field3] = value;
|
|
return std::tuple<decltype(field0), decltype(field1), decltype(field2),
|
|
decltype(field3)>(field0, field1, field2, field3);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct FieldAccessor<5> {
|
|
template <typename T>
|
|
static auto Get(T& value) -> auto {
|
|
auto& [field0, field1, field2, field3, field4] = value;
|
|
return std::tuple<decltype(field0), decltype(field1), decltype(field2),
|
|
decltype(field3), decltype(field4)>(
|
|
field0, field1, field2, field3, field4);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct FieldAccessor<6> {
|
|
template <typename T>
|
|
static auto Get(T& value) -> auto {
|
|
auto& [field0, field1, field2, field3, field4, field5] = value;
|
|
return std::tuple<decltype(field0), decltype(field1), decltype(field2),
|
|
decltype(field3), decltype(field4), decltype(field5)>(
|
|
field0, field1, field2, field3, field4, field5);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct FieldAccessor<7> {
|
|
template <typename T>
|
|
static auto Get(T& value) -> auto {
|
|
auto& [field0, field1, field2, field3, field4, field5, field6] = value;
|
|
return std::tuple<decltype(field0), decltype(field1), decltype(field2),
|
|
decltype(field3), decltype(field4), decltype(field5),
|
|
decltype(field6)>(field0, field1, field2, field3, field4,
|
|
field5, field6);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct FieldAccessor<8> {
|
|
template <typename T>
|
|
static auto Get(T& value) -> auto {
|
|
auto& [field0, field1, field2, field3, field4, field5, field6, field7] =
|
|
value;
|
|
return std::tuple<decltype(field0), decltype(field1), decltype(field2),
|
|
decltype(field3), decltype(field4), decltype(field5),
|
|
decltype(field6), decltype(field7)>(
|
|
field0, field1, field2, field3, field4, field5, field6, field7);
|
|
}
|
|
};
|
|
|
|
} // namespace Internal
|
|
|
|
// Get the fields of the struct `T` as a tuple.
|
|
template <typename T>
|
|
auto AsTuple(T value) -> auto {
|
|
// We use aggregate initialization to detect the number of fields.
|
|
static_assert(std::is_aggregate_v<T>, "Only aggregates are supported");
|
|
return Internal::FieldAccessor<Internal::CountFields<T>()>::Get(value);
|
|
}
|
|
|
|
} // namespace Carbon::StructReflection
|
|
|
|
#endif // CARBON_COMMON_STRUCT_REFLECTION_H_
|