From 23d10855ff2bae375bae39f060f975e80e6fd7d1 Mon Sep 17 00:00:00 2001 From: PragmaTwice Date: Mon, 27 Nov 2023 16:32:16 +0900 Subject: [PATCH 1/2] [C++] add duplicated fields detection in FURY_FIELD_INFO macro --- src/fury/encoder/row_encoder.h | 23 ++++-------- src/fury/meta/field_info.h | 30 +++++++++++----- src/fury/meta/type_traits.h | 64 ++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 26 deletions(-) create mode 100644 src/fury/meta/type_traits.h diff --git a/src/fury/encoder/row_encoder.h b/src/fury/encoder/row_encoder.h index 5f471647f5..a7d68d57d7 100644 --- a/src/fury/encoder/row_encoder.h +++ b/src/fury/encoder/row_encoder.h @@ -17,6 +17,7 @@ #pragma once #include "fury/meta/field_info.h" +#include "fury/meta/type_traits.h" #include "fury/row/row.h" #include "src/fury/row/writer.h" #include @@ -58,18 +59,6 @@ template <> struct ArrowSchemaBasicType { static inline constexpr const auto value = arrow::float64; }; -template struct RemoveMemberPointer; - -template struct RemoveMemberPointer { - using type = T; -}; - -template -using RemoveMemberPointerT = typename RemoveMemberPointer::type; - -template -using RemoveCVRefT = std::remove_cv_t>; - inline std::string StringViewToString(std::string_view s) { return {s.begin(), s.end()}; } @@ -104,8 +93,8 @@ struct RowEncodeTrait>> { static arrow::FieldVector FieldVectorImpl(std::index_sequence) { return {arrow::field( details::StringViewToString(FieldInfo::Names[I]), - RowEncodeTrait(FieldInfo::Ptrs))>>>::Type())...}; + RowEncodeTrait( + FieldInfo::Ptrs))>>::Type())...}; } static auto FieldVector() { @@ -122,9 +111,9 @@ struct RowEncodeTrait>> { template static auto WriteImpl(const T &value, RowWriter &writer, std::index_sequence) { - (RowEncodeTrait(FieldInfo::Ptrs))>>>:: - Write(value.*std::get(FieldInfo::Ptrs), writer, I), + (RowEncodeTrait( + FieldInfo::Ptrs))>>::Write(value.*std::get(FieldInfo::Ptrs), writer, + I), ...); } diff --git a/src/fury/meta/field_info.h b/src/fury/meta/field_info.h index 046f254d1c..917b28f6fc 100644 --- a/src/fury/meta/field_info.h +++ b/src/fury/meta/field_info.h @@ -17,33 +17,42 @@ #pragma once #include "fury/meta/preprocessor.h" +#include "fury/meta/type_traits.h" #include #include #include #include +#include namespace fury { namespace meta { -namespace details { - -// dependent name for constant `false` to workaround static_assert(false) issue -// before C++23 -template constexpr inline bool AlwaysFalse = false; - -} // namespace details - // decltype(FuryFieldInfo(v)) records field meta information for type T // it includes: // - number of fields: typed size_t // - field names: typed `std::string_view` // - field member points: typed `decltype(a) T::*` for any member `T::a` template constexpr auto FuryFieldInfo(const T &) noexcept { - static_assert(details::AlwaysFalse, + static_assert(AlwaysFalse, "FURY_FIELD_INFO for type T is expected but not defined"); } +namespace details { + +// it must be able to be executed in compile-time +template +constexpr bool IsValidFieldInfoImpl(std::index_sequence) { + return IsUnique(FieldInfo::Ptrs)...>::value; +} + +} // namespace details + +template constexpr bool IsValidFieldInfo() { + return details::IsValidFieldInfoImpl( + std::make_index_sequence{}); +} + } // namespace meta } // namespace fury @@ -67,6 +76,9 @@ template constexpr auto FuryFieldInfo(const T &) noexcept { static inline constexpr auto Ptrs = std::tuple{ \ FURY_PP_FOREACH_1(FURY_FIELD_INFO_PTRS_FUNC, type, __VA_ARGS__)}; \ }; \ + static_assert( \ + fury::meta::IsValidFieldInfo>(), \ + "duplicated fields in FURY_FIELD_INFO arguments are detected"); \ inline constexpr auto FuryFieldInfo(const type &) noexcept { \ return FuryFieldInfoImpl{}; \ }; diff --git a/src/fury/meta/type_traits.h b/src/fury/meta/type_traits.h new file mode 100644 index 0000000000..a4b5f7c21e --- /dev/null +++ b/src/fury/meta/type_traits.h @@ -0,0 +1,64 @@ +/* + * Copyright 2023 The Fury Authors + * + * 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 + * + * http://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. + */ + +#pragma once + +#include + +namespace fury { + +namespace meta { + +// dependent name for constant `false` to workaround static_assert(false) issue +// before C++23 +template constexpr inline bool AlwaysFalse = false; + +// T U::* -> T +template struct RemoveMemberPointer; + +template struct RemoveMemberPointer { + using type = T; +}; + +template +using RemoveMemberPointerT = typename RemoveMemberPointer::type; + +// same as std::remove_cvref_t since C++20 +template +using RemoveCVRefT = std::remove_cv_t>; + +template +using RemoveMemberPointerCVRefT = RemoveMemberPointerT>; + +template +inline constexpr bool IsSameValue = + std::is_same_v, + std::integral_constant>; + +template +inline constexpr bool ContainsValue = + std::disjunction_v>...>; + +template struct IsUnique : std::true_type {}; + +template +struct IsUnique + : std::bool_constant && IsUnique::value> { +}; + +} // namespace meta + +} // namespace fury From 8a6834e6af10e9253254f14f19b5a12db5e0679b Mon Sep 17 00:00:00 2001 From: PragmaTwice Date: Mon, 27 Nov 2023 17:17:25 +0900 Subject: [PATCH 2/2] add test for type_traits.h --- src/fury/meta/BUILD | 10 +++++ src/fury/meta/type_traits_test.cc | 70 +++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 src/fury/meta/type_traits_test.cc diff --git a/src/fury/meta/BUILD b/src/fury/meta/BUILD index d2d59cc8f8..81b8b41477 100644 --- a/src/fury/meta/BUILD +++ b/src/fury/meta/BUILD @@ -29,3 +29,13 @@ cc_test( "@com_google_googletest//:gtest", ], ) + +cc_test( + name = "type_traits_test", + srcs = ["type_traits_test.cc"], + copts = COPTS, + deps = [ + ":fury_meta", + "@com_google_googletest//:gtest", + ], +) diff --git a/src/fury/meta/type_traits_test.cc b/src/fury/meta/type_traits_test.cc new file mode 100644 index 0000000000..54a94f1de6 --- /dev/null +++ b/src/fury/meta/type_traits_test.cc @@ -0,0 +1,70 @@ +/* + * Copyright 2023 The Fury Authors + * + * 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 + * + * http://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 "gtest/gtest.h" + +#include "fury/meta/field_info.h" +#include "src/fury/meta/type_traits.h" + +namespace fury { + +namespace test { + +using namespace meta; + +struct A { + int x; + float y; +}; + +TEST(Meta, RemoveMemberPointer) { + static_assert(std::is_same_v, int>); + static_assert(std::is_same_v, bool>); +} + +TEST(Meta, IsSameValue) { + static_assert(IsSameValue<&A::x, &A::x>); + static_assert(!IsSameValue<&A::x, &A::y>); + + static_assert(!IsSameValue<1, true>); + static_assert(IsSameValue<2, 2>); +} + +TEST(Meta, ContainsValue) { + static_assert(ContainsValue<1, 1, 2, 3>); + static_assert(ContainsValue<2, 1, 2, 3>); + static_assert(ContainsValue<3, 1, 2, 3>); + static_assert(!ContainsValue<4, 1, 2, 3>); + static_assert(ContainsValue); + static_assert(ContainsValue<'a', 1, true, &A::x, 'a'>); + static_assert(!ContainsValue<0, 1, true, &A::x, 'a'>); +} + +TEST(Meta, IsUnique) { + static_assert(IsUnique<1, 2, 3>::value); + static_assert(IsUnique<1, false, true, 3, &A::x>::value); + static_assert(!IsUnique<1, false, true, false, &A::x>::value); + static_assert(!IsUnique<1, false, true, &A::x, 1>::value); +} + +} // namespace test + +} // namespace fury + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}