diff --git a/common/BUILD b/common/BUILD index a71435d45..977c09f6a 100644 --- a/common/BUILD +++ b/common/BUILD @@ -454,24 +454,9 @@ cc_test( cc_library( name = "native_type", - srcs = ["native_type.cc"], hdrs = ["native_type.h"], deps = [ - "@com_google_absl//absl/base", - "@com_google_absl//absl/base:config", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/meta:type_traits", - "@com_google_absl//absl/strings", - ], -) - -cc_test( - name = "native_type_test", - srcs = ["native_type_test.cc"], - deps = [ - ":native_type", - "//internal:testing", - "@com_google_absl//absl/hash:hash_testing", + ":typeinfo", ], ) @@ -1031,3 +1016,28 @@ cc_test( "@com_google_protobuf//:protobuf", ], ) + +cc_library( + name = "typeinfo", + srcs = ["typeinfo.cc"], + hdrs = ["typeinfo.h"], + deps = [ + "@com_google_absl//absl/base", + "@com_google_absl//absl/base:config", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/base:nullability", + "@com_google_absl//absl/meta:type_traits", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "typeinfo_test", + srcs = ["typeinfo_test.cc"], + deps = [ + ":typeinfo", + "//internal:testing", + "@com_google_absl//absl/hash:hash_testing", + "@com_google_absl//absl/strings", + ], +) diff --git a/common/native_type.h b/common/native_type.h index 94750f677..96c53c1da 100644 --- a/common/native_type.h +++ b/common/native_type.h @@ -15,177 +15,11 @@ #ifndef THIRD_PARTY_CEL_CPP_COMMON_NATIVE_TYPE_H_ #define THIRD_PARTY_CEL_CPP_COMMON_NATIVE_TYPE_H_ -#include -#include -#include -#include -#include - -#include "absl/base/attributes.h" -#include "absl/base/casts.h" // IWYU pragma: keep -#include "absl/base/config.h" -#include "absl/meta/type_traits.h" - -#if ABSL_HAVE_FEATURE(cxx_rtti) -#define CEL_INTERNAL_HAVE_RTTI 1 -#elif defined(__GNUC__) && defined(__GXX_RTTI) -#define CEL_INTERNAL_HAVE_RTTI 1 -#elif defined(_MSC_VER) && defined(_CPPRTTI) -#define CEL_INTERNAL_HAVE_RTTI 1 -#elif !defined(__GNUC__) && !defined(_MSC_VER) -#define CEL_INTERNAL_HAVE_RTTI 1 -#endif - -#ifdef CEL_INTERNAL_HAVE_RTTI -#include -#endif +#include "common/typeinfo.h" namespace cel { -template -struct NativeTypeTraits; - -class ABSL_ATTRIBUTE_TRIVIAL_ABI NativeTypeId final { - private: - template - struct HasNativeTypeTraitsId : std::false_type {}; - - template - struct HasNativeTypeTraitsId::Id( - std::declval()))>> - : std::true_type {}; - - template - static constexpr bool HasNativeTypeTraitsIdV = - HasNativeTypeTraitsId::value; - - public: - template - static NativeTypeId For() { - static_assert(!std::is_pointer_v); - static_assert(std::is_same_v>); - static_assert(!std::is_same_v>); -#ifdef CEL_INTERNAL_HAVE_RTTI - return NativeTypeId(&typeid(T)); -#else - // Adapted from Abseil and GTL. I believe this not being const is to ensure - // the compiler does not merge multiple constants with the same value to - // share the same address. - static char rep; - return NativeTypeId(&rep); -#endif - } - - // Gets the NativeTypeId for `T` at runtime. Requires that - // `cel::NativeTypeTraits` is defined for `T`. - template - static std::enable_if_t>, - NativeTypeId> - Of(const T& type) noexcept { - static_assert(!std::is_pointer_v); - static_assert(std::is_same_v>); - static_assert(!std::is_same_v>); - return NativeTypeTraits>::Id(type); - } - - // Gets the NativeTypeId for `T` at runtime. Requires that - // `cel::NativeTypeTraits` is defined for `T`. - template - static std::enable_if_t< - std::conjunction_v< - std::negation>>, - std::is_final>>, - NativeTypeId> - Of(const T&) noexcept { - static_assert(!std::is_pointer_v); - static_assert(std::is_same_v>); - static_assert(!std::is_same_v>); - return NativeTypeId::For>(); - } - - NativeTypeId() = default; - NativeTypeId(const NativeTypeId&) = default; - NativeTypeId(NativeTypeId&&) noexcept = default; - NativeTypeId& operator=(const NativeTypeId&) = default; - NativeTypeId& operator=(NativeTypeId&&) noexcept = default; - - std::string DebugString() const; - - friend bool operator==(NativeTypeId lhs, NativeTypeId rhs) { -#ifdef CEL_INTERNAL_HAVE_RTTI - return lhs.rep_ == rhs.rep_ || - (lhs.rep_ != nullptr && rhs.rep_ != nullptr && - *lhs.rep_ == *rhs.rep_); -#else - return lhs.rep_ == rhs.rep_; -#endif - } - - template - friend H AbslHashValue(H state, NativeTypeId id) { -#ifdef CEL_INTERNAL_HAVE_RTTI - return H::combine(std::move(state), - id.rep_ != nullptr ? id.rep_->hash_code() : size_t{0}); -#else - return H::combine(std::move(state), absl::bit_cast(id.rep_)); -#endif - } - - private: -#ifdef CEL_INTERNAL_HAVE_RTTI - constexpr explicit NativeTypeId(const std::type_info* rep) : rep_(rep) {} - - const std::type_info* rep_ = nullptr; -#else - constexpr explicit NativeTypeId(const void* rep) : rep_(rep) {} - - const void* rep_ = nullptr; -#endif -}; - -inline bool operator!=(NativeTypeId lhs, NativeTypeId rhs) { - return !operator==(lhs, rhs); -} - -inline std::ostream& operator<<(std::ostream& out, NativeTypeId id) { - return out << id.DebugString(); -} - -class NativeType final { - public: - // Determines at runtime whether calling the destructor of `T` can be skipped - // when `T` was allocated by a pooling memory manager. - template - ABSL_MUST_USE_RESULT static bool SkipDestructor(const T& type) { - if constexpr (std::is_trivially_destructible_v) { - return true; - } else if constexpr (HasNativeTypeTraitsSkipDestructorV) { - return NativeTypeTraits::SkipDestructor(type); - } else { - return false; - } - } - - private: - template - struct HasNativeTypeTraitsSkipDestructor : std::false_type {}; - - template - struct HasNativeTypeTraitsSkipDestructor< - T, std::void_t::SkipDestructor( - std::declval()))>> : std::true_type {}; - - template - static inline constexpr bool HasNativeTypeTraitsSkipDestructorV = - HasNativeTypeTraitsSkipDestructor::value; - - NativeType() = delete; - NativeType(const NativeType&) = delete; - NativeType(NativeType&&) = delete; - ~NativeType() = delete; - NativeType& operator=(const NativeType&) = delete; - NativeType& operator=(NativeType&&) = delete; -}; +using NativeTypeId = TypeInfo; } // namespace cel diff --git a/common/native_type_test.cc b/common/native_type_test.cc deleted file mode 100644 index 0de09f224..000000000 --- a/common/native_type_test.cc +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2023 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "common/native_type.h" - -#include -#include - -#include "absl/hash/hash_testing.h" -#include "internal/testing.h" - -namespace cel { -namespace { - -using ::testing::IsEmpty; -using ::testing::Not; -using ::testing::SizeIs; - -struct Type1 {}; - -struct Type2 {}; - -struct Type3 {}; - -TEST(NativeTypeId, ImplementsAbslHashCorrectly) { - EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( - {NativeTypeId(), NativeTypeId::For(), NativeTypeId::For(), - NativeTypeId::For()})); -} - -TEST(NativeTypeId, DebugString) { - std::ostringstream out; - out << NativeTypeId(); - EXPECT_THAT(out.str(), IsEmpty()); - out << NativeTypeId::For(); - auto string = out.str(); - EXPECT_THAT(string, Not(IsEmpty())); - EXPECT_THAT(string, SizeIs(std::strlen(string.c_str()))); -} - -struct TestType {}; - -} // namespace - -template <> -struct NativeTypeTraits final { - static NativeTypeId Id(const TestType&) { - return NativeTypeId::For(); - } -}; - -namespace { - -TEST(NativeTypeId, Of) { - EXPECT_EQ(NativeTypeId::Of(TestType()), NativeTypeId::For()); -} - -struct TrivialObject {}; - -TEST(NativeType, SkipDestructorTrivial) { - EXPECT_TRUE(NativeType::SkipDestructor(TrivialObject{})); -} - -struct NonTrivialObject { - // Not "= default" on purpose to make this non-trivial. - // NOLINTNEXTLINE(modernize-use-equals-default) - ~NonTrivialObject() {} -}; - -TEST(NativeType, SkipDestructorNonTrivial) { - EXPECT_FALSE(NativeType::SkipDestructor(NonTrivialObject{})); -} - -struct SkippableDestructObject { - // Not "= default" on purpose to make this non-trivial. - // NOLINTNEXTLINE(modernize-use-equals-default) - ~SkippableDestructObject() {} -}; - -} // namespace - -template <> -struct NativeTypeTraits final { - static bool SkipDestructor(const SkippableDestructObject&) { return true; } -}; - -namespace { - -TEST(NativeType, SkipDestructorTraits) { - EXPECT_TRUE(NativeType::SkipDestructor(SkippableDestructObject{})); -} - -} // namespace - -} // namespace cel diff --git a/common/native_type.cc b/common/typeinfo.cc similarity index 96% rename from common/native_type.cc rename to common/typeinfo.cc index 16a84101f..86bae1934 100644 --- a/common/native_type.cc +++ b/common/typeinfo.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "common/native_type.h" +#include "common/typeinfo.h" #include #include // IWYU pragma: keep @@ -44,7 +44,7 @@ struct FreeDeleter { } // namespace -std::string NativeTypeId::DebugString() const { +std::string TypeInfo::DebugString() const { if (rep_ == nullptr) { return std::string(); } diff --git a/common/typeinfo.h b/common/typeinfo.h new file mode 100644 index 000000000..f5dfd1556 --- /dev/null +++ b/common/typeinfo.h @@ -0,0 +1,198 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_CEL_CPP_COMMON_TYPEINFO_H_ +#define THIRD_PARTY_CEL_CPP_COMMON_TYPEINFO_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/casts.h" // IWYU pragma: keep +#include "absl/base/config.h" +#include "absl/base/nullability.h" +#include "absl/meta/type_traits.h" + +#if ABSL_HAVE_FEATURE(cxx_rtti) +#define CEL_INTERNAL_HAVE_RTTI 1 +#elif defined(__GNUC__) && defined(__GXX_RTTI) +#define CEL_INTERNAL_HAVE_RTTI 1 +#elif defined(_MSC_VER) && defined(_CPPRTTI) +#define CEL_INTERNAL_HAVE_RTTI 1 +#elif !defined(__GNUC__) && !defined(_MSC_VER) +#define CEL_INTERNAL_HAVE_RTTI 1 +#endif + +#ifdef CEL_INTERNAL_HAVE_RTTI +#include +#endif + +namespace cel { + +class TypeInfo; + +template +struct NativeTypeTraits; + +namespace common_internal { + +template +struct HasNativeTypeTraitsId : std::false_type {}; + +template +struct HasNativeTypeTraitsId< + T, std::void_t::Id(std::declval()))>> + : std::true_type {}; + +template +static constexpr bool HasNativeTypeTraitsIdV = HasNativeTypeTraitsId::value; + +template +struct HasCelTypeId : std::false_type {}; + +template +struct HasCelTypeId< + T, std::enable_if_t()))>, + TypeInfo>>> : std::true_type {}; + +} // namespace common_internal + +template +TypeInfo TypeId(); + +template +std::enable_if_t< + std::conjunction_v, + std::negation>>, + TypeInfo> +TypeId(const T& t) { + return NativeTypeTraits>::Id(t); +} + +template +std::enable_if_t< + std::conjunction_v>, + std::negation>, + std::is_final>, + TypeInfo> +TypeId(const T& t) { + return cel::TypeId>(); +} + +template +std::enable_if_t< + std::conjunction_v>, + common_internal::HasCelTypeId>, + TypeInfo> +TypeId(const T& t) { + return CelTypeId(t); +} + +class TypeInfo final { + public: + template + ABSL_DEPRECATED("Use cel::TypeId() instead") + static TypeInfo For() { + return cel::TypeId(); + } + + template + ABSL_DEPRECATED("Use cel::TypeId(...) instead") + static TypeInfo Of(const T& type) { + return cel::TypeId(type); + } + + TypeInfo() = default; + TypeInfo(const TypeInfo&) = default; + TypeInfo& operator=(const TypeInfo&) = default; + + std::string DebugString() const; + + template + friend void AbslStringify(S& sink, TypeInfo type_info) { + sink.Append(type_info.DebugString()); + } + + friend constexpr bool operator==(TypeInfo lhs, TypeInfo rhs) noexcept { +#ifdef CEL_INTERNAL_HAVE_RTTI + return lhs.rep_ == rhs.rep_ || + (lhs.rep_ != nullptr && rhs.rep_ != nullptr && + *lhs.rep_ == *rhs.rep_); +#else + return lhs.rep_ == rhs.rep_; +#endif + } + + template + friend H AbslHashValue(H state, TypeInfo id) { +#ifdef CEL_INTERNAL_HAVE_RTTI + return H::combine(std::move(state), + id.rep_ != nullptr ? id.rep_->hash_code() : size_t{0}); +#else + return H::combine(std::move(state), absl::bit_cast(id.rep_)); +#endif + } + + private: + template + friend TypeInfo TypeId(); + +#ifdef CEL_INTERNAL_HAVE_RTTI + constexpr explicit TypeInfo(const std::type_info* ABSL_NULLABLE rep) + : rep_(rep) {} + + const std::type_info* ABSL_NULLABLE rep_ = nullptr; +#else + constexpr explicit TypeInfo(const void* ABSL_NULLABLE rep) : rep_(rep) {} + + const void* ABSL_NULLABLE rep_ = nullptr; +#endif +}; + +#ifndef CEL_INTERNAL_HAVE_RTTI +namespace common_internal { +template +struct TypeTag final { + static constexpr char value = 0; +}; +} // namespace common_internal +#endif + +template +TypeInfo TypeId() { + static_assert(!std::is_pointer_v); + static_assert(std::is_same_v>); + static_assert(!std::is_same_v>); +#ifdef CEL_INTERNAL_HAVE_RTTI + return TypeInfo(&typeid(T)); +#else + return TypeInfo(&common_internal::TypeTag::value); +#endif +} + +inline constexpr bool operator!=(TypeInfo lhs, TypeInfo rhs) noexcept { + return !operator==(lhs, rhs); +} + +inline std::ostream& operator<<(std::ostream& out, TypeInfo id) { + return out << id.DebugString(); +} + +} // namespace cel + +#endif // THIRD_PARTY_CEL_CPP_COMMON_TYPEINFO_H_ diff --git a/common/typeinfo_test.cc b/common/typeinfo_test.cc new file mode 100644 index 000000000..cf5b5f877 --- /dev/null +++ b/common/typeinfo_test.cc @@ -0,0 +1,75 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/typeinfo.h" + +#include +#include + +#include "absl/hash/hash_testing.h" +#include "absl/strings/str_cat.h" +#include "internal/testing.h" + +namespace cel { +namespace { + +using ::testing::IsEmpty; +using ::testing::Not; +using ::testing::SizeIs; + +struct Type1 {}; + +struct Type2 {}; + +struct Type3 {}; + +TEST(TypeInfo, ImplementsAbslHashCorrectly) { + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + {TypeInfo(), cel::TypeId(), cel::TypeId(), + cel::TypeId()})); +} + +TEST(TypeInfo, Ostream) { + std::ostringstream out; + out << TypeInfo(); + EXPECT_THAT(out.str(), IsEmpty()); + out << cel::TypeId(); + auto string = out.str(); + EXPECT_THAT(string, Not(IsEmpty())); + EXPECT_THAT(string, SizeIs(std::strlen(string.c_str()))); +} + +TEST(TypeInfo, AbslStringify) { + EXPECT_THAT(absl::StrCat(TypeInfo()), IsEmpty()); + EXPECT_THAT(absl::StrCat(cel::TypeId()), Not(IsEmpty())); +} + +struct TestType {}; + +} // namespace + +template <> +struct NativeTypeTraits final { + static TypeInfo Id(const TestType&) { return cel::TypeId(); } +}; + +namespace { + +TEST(TypeInfo, Of) { + EXPECT_EQ(cel::TypeId(TestType()), cel::TypeId()); +} + +} // namespace + +} // namespace cel diff --git a/common/types/function_type.h b/common/types/function_type.h index 055bb1e4d..c649dbd6b 100644 --- a/common/types/function_type.h +++ b/common/types/function_type.h @@ -25,7 +25,6 @@ #include "absl/base/nullability.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" -#include "common/native_type.h" #include "common/type_kind.h" #include "google/protobuf/arena.h" @@ -67,8 +66,6 @@ class FunctionType final { explicit operator bool() const { return data_ != nullptr; } private: - friend struct NativeTypeTraits; - explicit FunctionType( const common_internal::FunctionTypeData* ABSL_NULLABLE data) : data_(data) {} @@ -89,13 +86,6 @@ inline std::ostream& operator<<(std::ostream& out, const FunctionType& type) { return out << type.DebugString(); } -template <> -struct NativeTypeTraits final { - static bool SkipDestructor(const FunctionType& type) { - return NativeType::SkipDestructor(type.data_); - } -}; - } // namespace cel #endif // THIRD_PARTY_CEL_CPP_COMMON_TYPES_FUNCTION_TYPE_H_