diff --git a/core/foundation/inc/ROOT/TypeTraits.hxx b/core/foundation/inc/ROOT/TypeTraits.hxx index c86e39b9f5ef8aa14427fbe041ebbb9a9d31b474..1abf64e73a89c0e3056d1ba94fd9eb91e81a994e 100644 --- a/core/foundation/inc/ROOT/TypeTraits.hxx +++ b/core/foundation/inc/ROOT/TypeTraits.hxx @@ -14,8 +14,6 @@ #include <memory> // shared_ptr, unique_ptr for IsSmartOrDumbPtr #include <type_traits> -#include <vector> // for IsContainer -#include "ROOT/RSpan.hxx" // for IsContainer namespace ROOT { @@ -93,42 +91,6 @@ template <class P> class IsSmartOrDumbPtr<std::unique_ptr<P>> : public std::true_type { }; -/// Check for container traits. -/// -/// Note that this trait selects std::string as container. -template <typename T> -struct IsContainer { - using Test_t = typename std::decay<T>::type; - - template <typename A> - static constexpr bool Test(A *pt, A const *cpt = nullptr, decltype(pt->begin()) * = nullptr, - decltype(pt->end()) * = nullptr, decltype(cpt->begin()) * = nullptr, - decltype(cpt->end()) * = nullptr, typename A::iterator *pi = nullptr, - typename A::const_iterator *pci = nullptr) - { - using It_t = typename A::iterator; - using CIt_t = typename A::const_iterator; - using V_t = typename A::value_type; - return std::is_same<Test_t, std::vector<bool>>::value || - (std::is_same<decltype(pt->begin()), It_t>::value && std::is_same<decltype(pt->end()), It_t>::value && - std::is_same<decltype(cpt->begin()), CIt_t>::value && std::is_same<decltype(cpt->end()), CIt_t>::value && - std::is_same<decltype(**pi), V_t &>::value && std::is_same<decltype(**pci), V_t const &>::value); - } - - template <typename A> - static constexpr bool Test(...) - { - return false; - } - - static constexpr bool value = Test<Test_t>(nullptr); -}; - -template<typename T> -struct IsContainer<std::span<T>> { - static constexpr bool value = true; -}; - /// Checks for signed integers types that are not characters template<class T> struct IsSignedNumeral : std::integral_constant<bool, diff --git a/core/foundation/test/testTypeTraits.cxx b/core/foundation/test/testTypeTraits.cxx index f9a9b55bce26385fb96dfb213da03eba5885e068..ef00c68f5df2c629475a2257b28a37d83575a6e0 100644 --- a/core/foundation/test/testTypeTraits.cxx +++ b/core/foundation/test/testTypeTraits.cxx @@ -41,14 +41,6 @@ TEST(TypeTraits, RemoveFirstParameter) ::testing::StaticAssertTypeEq<RemoveFirstParameter_t<std::tuple<void>>, std::tuple<>>(); } -TEST(TypeTraits, IsContainer) -{ - static_assert(IsContainer<std::vector<int>>::value, ""); - static_assert(IsContainer<std::vector<bool>>::value, ""); - static_assert(IsContainer<std::tuple<int, int>>::value == false, ""); - static_assert(IsContainer<std::string>::value, ""); -} - /******** helper objects ***********/ struct Dummy { }; diff --git a/tree/dataframe/inc/ROOT/RDF/ActionHelpers.hxx b/tree/dataframe/inc/ROOT/RDF/ActionHelpers.hxx index 297abddf9ae449846d27e1aca2b14f0c0fdae501..c52178a365ce124a02254df461cbfac1aaaf225f 100644 --- a/tree/dataframe/inc/ROOT/RDF/ActionHelpers.hxx +++ b/tree/dataframe/inc/ROOT/RDF/ActionHelpers.hxx @@ -182,7 +182,7 @@ public: void Exec(unsigned int slot, double v); void Exec(unsigned int slot, double v, double w); - template <typename T, typename std::enable_if<IsContainer<T>::value, int>::type = 0> + template <typename T, typename std::enable_if<IsDataContainer<T>::value || std::is_same<T, std::string>::value, int>::type = 0> void Exec(unsigned int slot, const T &vs) { auto &thisBuf = fBuffers[slot]; @@ -193,7 +193,7 @@ public: } template <typename T, typename W, - typename std::enable_if<IsContainer<T>::value && IsContainer<W>::value, int>::type = 0> + typename std::enable_if<IsDataContainer<T>::value && IsDataContainer<W>::value, int>::type = 0> void Exec(unsigned int slot, const T &vs, const W &ws) { auto &thisBuf = fBuffers[slot]; @@ -210,7 +210,7 @@ public: } template <typename T, typename W, - typename std::enable_if<IsContainer<T>::value && !IsContainer<W>::value, int>::type = 0> + typename std::enable_if<IsDataContainer<T>::value && !IsDataContainer<W>::value, int>::type = 0> void Exec(unsigned int slot, const T &vs, const W w) { auto &thisBuf = fBuffers[slot]; @@ -225,7 +225,7 @@ public: // ROOT-10092: Filling with a scalar as first column and a collection as second is not supported template <typename T, typename W, - typename std::enable_if<IsContainer<W>::value && !IsContainer<T>::value, int>::type = 0> + typename std::enable_if<IsDataContainer<W>::value && !IsDataContainer<T>::value, int>::type = 0> void Exec(unsigned int, const T &, const W &) { throw std::runtime_error( @@ -295,7 +295,7 @@ public: fObjects[slot]->Fill(x0, x1, x2, x3); } - template <typename X0, typename std::enable_if<IsContainer<X0>::value, int>::type = 0> + template <typename X0, typename std::enable_if<IsDataContainer<X0>::value || std::is_same<X0, std::string>::value, int>::type = 0> void Exec(unsigned int slot, const X0 &x0s) { auto thisSlotH = fObjects[slot]; @@ -306,7 +306,7 @@ public: // ROOT-10092: Filling with a scalar as first column and a collection as second is not supported template <typename X0, typename X1, - typename std::enable_if<IsContainer<X1>::value && !IsContainer<X0>::value, int>::type = 0> + typename std::enable_if<IsDataContainer<X1>::value && !IsDataContainer<X0>::value, int>::type = 0> void Exec(unsigned int , const X0 &, const X1 &) { throw std::runtime_error( @@ -314,7 +314,7 @@ public: } template <typename X0, typename X1, - typename std::enable_if<IsContainer<X0>::value && IsContainer<X1>::value, int>::type = 0> + typename std::enable_if<IsDataContainer<X0>::value && IsDataContainer<X1>::value, int>::type = 0> void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s) { auto thisSlotH = fObjects[slot]; @@ -330,7 +330,7 @@ public: } template <typename X0, typename W, - typename std::enable_if<IsContainer<X0>::value && !IsContainer<W>::value, int>::type = 0> + typename std::enable_if<IsDataContainer<X0>::value && !IsDataContainer<W>::value, int>::type = 0> void Exec(unsigned int slot, const X0 &x0s, const W w) { auto thisSlotH = fObjects[slot]; @@ -340,7 +340,7 @@ public: } template <typename X0, typename X1, typename X2, - typename std::enable_if<IsContainer<X0>::value && IsContainer<X1>::value && IsContainer<X2>::value, + typename std::enable_if<IsDataContainer<X0>::value && IsDataContainer<X1>::value && IsDataContainer<X2>::value, int>::type = 0> void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s, const X2 &x2s) { @@ -358,7 +358,7 @@ public: } template <typename X0, typename X1, typename W, - typename std::enable_if<IsContainer<X0>::value && IsContainer<X1>::value && !IsContainer<W>::value, + typename std::enable_if<IsDataContainer<X0>::value && IsDataContainer<X1>::value && !IsDataContainer<W>::value, int>::type = 0> void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s, const W w) { @@ -375,8 +375,8 @@ public: } template <typename X0, typename X1, typename X2, typename X3, - typename std::enable_if<IsContainer<X0>::value && IsContainer<X1>::value && IsContainer<X2>::value && - IsContainer<X3>::value, + typename std::enable_if<IsDataContainer<X0>::value && IsDataContainer<X1>::value && IsDataContainer<X2>::value && + IsDataContainer<X3>::value, int>::type = 0> void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s, const X2 &x2s, const X3 &x3s) { @@ -395,8 +395,8 @@ public: } template <typename X0, typename X1, typename X2, typename W, - typename std::enable_if<IsContainer<X0>::value && IsContainer<X1>::value && IsContainer<X2>::value && - !IsContainer<W>::value, + typename std::enable_if<IsDataContainer<X0>::value && IsDataContainer<X1>::value && IsDataContainer<X2>::value && + !IsDataContainer<W>::value, int>::type = 0> void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s, const X2 &x2s, const W w) { @@ -459,8 +459,7 @@ public: void InitTask(TTreeReader *, unsigned int) {} template <typename X0, typename X1, - typename std::enable_if< - ROOT::TypeTraits::IsContainer<X0>::value && ROOT::TypeTraits::IsContainer<X1>::value, int>::type = 0> + typename std::enable_if<IsDataContainer<X0>::value && IsDataContainer<X1>::value, int>::type = 0> void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s) { if (x0s.size() != x1s.size()) { @@ -727,7 +726,7 @@ public: void InitTask(TTreeReader *, unsigned int) {} - template <typename T, typename std::enable_if<IsContainer<T>::value, int>::type = 0> + template <typename T, typename std::enable_if<IsDataContainer<T>::value, int>::type = 0> void Exec(unsigned int slot, const T &vs) { for (auto &&v : vs) @@ -771,7 +770,7 @@ public: void InitTask(TTreeReader *, unsigned int) {} void Exec(unsigned int slot, ResultType v) { fMaxs[slot] = std::max(v, fMaxs[slot]); } - template <typename T, typename std::enable_if<IsContainer<T>::value, int>::type = 0> + template <typename T, typename std::enable_if<IsDataContainer<T>::value, int>::type = 0> void Exec(unsigned int slot, const T &vs) { for (auto &&v : vs) @@ -831,7 +830,7 @@ public: void InitTask(TTreeReader *, unsigned int) {} void Exec(unsigned int slot, ResultType v) { fSums[slot] += v; } - template <typename T, typename std::enable_if<IsContainer<T>::value, int>::type = 0> + template <typename T, typename std::enable_if<IsDataContainer<T>::value, int>::type = 0> void Exec(unsigned int slot, const T &vs) { for (auto &&v : vs) @@ -864,7 +863,7 @@ public: void InitTask(TTreeReader *, unsigned int) {} void Exec(unsigned int slot, double v); - template <typename T, typename std::enable_if<IsContainer<T>::value, int>::type = 0> + template <typename T, typename std::enable_if<IsDataContainer<T>::value, int>::type = 0> void Exec(unsigned int slot, const T &vs) { for (auto &&v : vs) { @@ -906,7 +905,7 @@ public: void InitTask(TTreeReader *, unsigned int) {} void Exec(unsigned int slot, double v); - template <typename T, typename std::enable_if<IsContainer<T>::value, int>::type = 0> + template <typename T, typename std::enable_if<IsDataContainer<T>::value, int>::type = 0> void Exec(unsigned int slot, const T &vs) { for (auto &&v : vs) { diff --git a/tree/dataframe/inc/ROOT/RDF/InterfaceUtils.hxx b/tree/dataframe/inc/ROOT/RDF/InterfaceUtils.hxx index 06d0e355ed11c840332a724f4542ce8c3c04ffe8..47a3fd6458b2758f3c186ba8a717d82998230210 100644 --- a/tree/dataframe/inc/ROOT/RDF/InterfaceUtils.hxx +++ b/tree/dataframe/inc/ROOT/RDF/InterfaceUtils.hxx @@ -408,7 +408,7 @@ void CallBuildAction(std::shared_ptr<PrevNodeType> *prevNodeOnHeap, const Column } /// The contained `type` alias is `double` if `T == RInferredType`, `U` if `T == std::container<U>`, `T` otherwise. -template <typename T, bool Container = TTraits::IsContainer<T>::value && !std::is_same<T, std::string>::value> +template <typename T, bool Container = RDFInternal::IsDataContainer<T>::value && !std::is_same<T, std::string>::value> struct TMinReturnType { using type = T; }; diff --git a/tree/dataframe/inc/ROOT/RDF/RDisplay.hxx b/tree/dataframe/inc/ROOT/RDF/RDisplay.hxx index c88e8ae78c0d791a4fd35d10a6373cc593670488..90058f070660da51b74cd75e3ce3dfbed86458d3 100644 --- a/tree/dataframe/inc/ROOT/RDF/RDisplay.hxx +++ b/tree/dataframe/inc/ROOT/RDF/RDisplay.hxx @@ -96,7 +96,7 @@ private: /// \param[in] element The event to convert to its string representation /// \param[in] index To which column the event belongs to /// \return false, the event is not a collection - template <typename T, typename std::enable_if<std::is_same<T, std::string>::value || !ROOT::TypeTraits::IsContainer<T>::value, int>::type = 0> + template <typename T, typename std::enable_if<!ROOT::Internal::RDF::IsDataContainer<T>::value, int>::type = 0> bool AddInterpreterString(std::stringstream &stream, T &element, const int &index) { stream << "*((std::string*)" << ROOT::Internal::RDF::PrettyPrintAddr(&(fRepresentations[index])) @@ -112,7 +112,7 @@ private: /// \param[in] index To which column the event belongs to /// \return true, the event is a collection /// This function chains a sequence of call to cling::printValue, one for each element of the collection. - template <typename T, typename std::enable_if<!std::is_same<T, std::string>::value && ROOT::TypeTraits::IsContainer<T>::value, int>::type = 0> + template <typename T, typename std::enable_if<ROOT::Internal::RDF::IsDataContainer<T>::value, int>::type = 0> bool AddInterpreterString(std::stringstream &stream, T &collection, const int &index) { size_t collectionSize = std::distance(std::begin(collection), std::end(collection)); diff --git a/tree/dataframe/inc/ROOT/RDF/Utils.hxx b/tree/dataframe/inc/ROOT/RDF/Utils.hxx index fcd36bda30128e9747ba8c8b92020192f4fa9035..9fbe5eee5d7b6c9cf3b056b0cc1ab36bc4f5b041 100644 --- a/tree/dataframe/inc/ROOT/RDF/Utils.hxx +++ b/tree/dataframe/inc/ROOT/RDF/Utils.hxx @@ -15,6 +15,7 @@ #include "ROOT/TypeTraits.hxx" #include "ROOT/RVec.hxx" #include "ROOT/RSnapshotOptions.hxx" +#include "ROOT/RSpan.hxx" // for IsDataContainer #include "TH1.h" #include <array> @@ -23,7 +24,6 @@ #include <memory> #include <string> #include <type_traits> // std::decay -#include <vector> class TTree; class TTreeReader; @@ -52,6 +52,43 @@ using namespace ROOT::TypeTraits; using namespace ROOT::Detail::RDF; using namespace ROOT::RDF; +/// Check for container traits. +/// +/// Note that we don't recognize std::string as a container. +template <typename T> +struct IsDataContainer { + using Test_t = typename std::decay<T>::type; + + template <typename A> + static constexpr bool Test(A *pt, A const *cpt = nullptr, decltype(pt->begin()) * = nullptr, + decltype(pt->end()) * = nullptr, decltype(cpt->begin()) * = nullptr, + decltype(cpt->end()) * = nullptr, typename A::iterator *pi = nullptr, + typename A::const_iterator *pci = nullptr) + { + using It_t = typename A::iterator; + using CIt_t = typename A::const_iterator; + using V_t = typename A::value_type; + return std::is_same<Test_t, std::vector<bool>>::value || + (std::is_same<decltype(pt->begin()), It_t>::value && std::is_same<decltype(pt->end()), It_t>::value && + std::is_same<decltype(cpt->begin()), CIt_t>::value && std::is_same<decltype(cpt->end()), CIt_t>::value && + std::is_same<decltype(**pi), V_t &>::value && std::is_same<decltype(**pci), V_t const &>::value && + !std::is_same<T, std::string>::value); + } + + template <typename A> + static constexpr bool Test(...) + { + return false; + } + + static constexpr bool value = Test<Test_t>(nullptr); +}; + +template<typename T> +struct IsDataContainer<std::span<T>> { + static constexpr bool value = true; +}; + /// Detect whether a type is an instantiation of vector<T,A> template <typename> struct IsVector_t : public std::false_type {}; @@ -107,7 +144,7 @@ struct IsRVec_t<ROOT::VecOps::RVec<T>> : public std::true_type {}; // Check the value_type type of a type with a SFINAE to allow compilation in presence // fundamental types -template <typename T, bool IsContainer = IsContainer<typename std::decay<T>::type>::value> +template <typename T, bool IsDataContainer = IsDataContainer<typename std::decay<T>::type>::value || std::is_same<std::string, T>::value> struct ValueType { using value_type = typename T::value_type; }; diff --git a/tree/dataframe/test/dataframe_utils.cxx b/tree/dataframe/test/dataframe_utils.cxx index 77477d10b0c8d1aa3a1ba88629f440ecdd1fc362..4e64efe5e803e94ca44a12e355c45bfc1c07c08e 100644 --- a/tree/dataframe/test/dataframe_utils.cxx +++ b/tree/dataframe/test/dataframe_utils.cxx @@ -240,3 +240,11 @@ TEST(RDataFrameUtils, FindUnknownColumnsFriendTrees) auto ncols = RDFInt::FindUnknownColumns({"c2", "c3", "c4"}, RDFInt::GetBranchNames(t1), {}, {}); EXPECT_EQ(ncols.size(), 0u) << "Cannot find column in friend trees."; } + +TEST(RDataFrameUtils, IsDataContainer) +{ + static_assert(RDFInt::IsDataContainer<std::vector<int>>::value, ""); + static_assert(RDFInt::IsDataContainer<std::vector<bool>>::value, ""); + static_assert(RDFInt::IsDataContainer<std::tuple<int, int>>::value == false, ""); + static_assert(RDFInt::IsDataContainer<std::string>::value == false, ""); +} diff --git a/tutorials/dataframe/df022_useKahan.C b/tutorials/dataframe/df022_useKahan.C index f0c4a2e8478116bbaee4a47449389e092e884427..577e9dfe23040b8624cbae606a97d9a854e86b49 100644 --- a/tutorials/dataframe/df022_useKahan.C +++ b/tutorials/dataframe/df022_useKahan.C @@ -52,7 +52,7 @@ public: KahanAlgorithm(x, fPartialSums[slot], fCompensations[slot]); } - template <typename V=T, typename std::enable_if<ROOT::TypeTraits::IsContainer<V>::value, int>::type = 0> + template <typename V=T, typename std::enable_if<ROOT::Internal::RDF::IsDataContainer<V>::value, int>::type = 0> void Exec(unsigned int slot, const T &vs) { for (auto &&v : vs) {