Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:02:32

0001 #pragma once
0002 
0003 #include <typeinfo>
0004 #include <type_traits>
0005 
0006 #include <algorithm>
0007 
0008 #include <string>
0009 #include <bitset>
0010 #include <utility>
0011 #include <tuple>
0012 #include <memory>
0013 #include <array>
0014 #include <vector>
0015 #include <deque>
0016 #include <forward_list>
0017 #include <list>
0018 #include <set>
0019 #include <unordered_set>
0020 #include <map>
0021 #include <unordered_map>
0022 
0023 #include <cstddef>
0024 #include <cmath>
0025 
0026 #include "CondFormats/Serialization/interface/Serializable.h"
0027 
0028 namespace cond {
0029   namespace serialization {
0030 
0031     template <typename T>
0032     bool equal(const T& first, const T& second) {
0033       // This function takes advantage of template argument deduction,
0034       // making it easier to use than the access<T> template.
0035 
0036       // It is also called by the access<T>::equal_() methods themselves
0037       // if they need to compare objects. This means all comparisons
0038       // pass by here.
0039 
0040       // Therefore, we could easily first check here whether the address of
0041       // the objects is the same or add debugging code. In our use case,
0042       // however, most of the objects will have different addresses.
0043       return access<T>::equal_(first, second);
0044     }
0045 
0046     template <typename T>
0047     struct access<T, typename std::enable_if<std::is_integral<T>::value or std::is_enum<T>::value>::type> {
0048       static bool equal_(const T first, const T second) { return first == second; }
0049     };
0050 
0051     template <typename T>
0052     struct access<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
0053       static bool equal_(const T first, const T second) {
0054         // TODO: we consider all NaNs to be equal -- should we even allow to serialize them?
0055         if (std::isnan(first) or std::isnan(second))
0056           return std::isnan(first) and std::isnan(second);
0057 
0058         if (std::isinf(first) or std::isinf(second))
0059           return std::isinf(first) and std::isinf(second) and std::signbit(first) == std::signbit(second);
0060 
0061         // TODO: consider expected precision for cross-platform serialization
0062         return first == second;
0063       }
0064     };
0065 
0066     template <>
0067     struct access<std::string> {
0068       static bool equal_(const std::string& first, const std::string& second) { return first == second; }
0069     };
0070 
0071     template <std::size_t N>
0072     struct access<std::bitset<N>> {
0073       static bool equal_(const std::bitset<N>& first, const std::bitset<N>& second) { return first == second; }
0074     };
0075 
0076     template <typename T, typename U>
0077     struct access<std::pair<T, U>> {
0078       static bool equal_(const std::pair<T, U>& first, const std::pair<T, U>& second) {
0079         return equal(first.first, second.first) and equal(first.second, second.second);
0080       }
0081     };
0082 
0083     template <std::size_t N, typename... Ts>
0084     struct equal_tuple {
0085       static bool equal_(const std::tuple<Ts...>& first, const std::tuple<Ts...>& second) {
0086         if (not equal(std::get<N - 1>(first), std::get<N - 1>(second)))
0087           return false;
0088 
0089         return equal_tuple<N - 1, Ts...>::equal_(first, second);
0090       }
0091     };
0092 
0093     template <typename... Ts>
0094     struct equal_tuple<0, Ts...> {
0095       static bool equal_(const std::tuple<Ts...>& first, const std::tuple<Ts...>& second) { return true; }
0096     };
0097 
0098     template <typename... Ts>
0099     struct access<std::tuple<Ts...>> {
0100       static bool equal_(const std::tuple<Ts...>& first, const std::tuple<Ts...>& second) {
0101         return equal_tuple<sizeof...(Ts), Ts...>::equal_(first, second);
0102       }
0103     };
0104 
0105     template <typename T>
0106     struct access<T, typename std::enable_if<std::is_pointer<T>::value>::type> {
0107       static bool equal_(const T first, const T second) {
0108         if (first == nullptr or second == nullptr)
0109           return first == second;
0110 
0111         // Compare the addresses first -- even if equal() does not
0112         // do it for all types, if we are serializing pointers we may
0113         // have some use case of containers of pointers to a small
0114         // set of real objects.
0115         return first == second or equal(*first, *second);
0116       }
0117     };
0118 
0119 #define equal_pointer(TYPE)                                                                                      \
0120   template <typename T>                                                                                          \
0121   struct access<TYPE<T>> {                                                                                       \
0122     static bool equal_(const TYPE<T>& first, const TYPE<T>& second) { return equal(first.get(), second.get()); } \
0123   };
0124 
0125     equal_pointer(std::unique_ptr);
0126     equal_pointer(std::shared_ptr);
0127 #undef equal_pointer
0128 
0129     template <typename T, std::size_t N>
0130     struct access<T[N]> {
0131       static bool equal_(const T (&first)[N], const T (&second)[N]) {
0132         for (std::size_t i = 0; i < N; ++i)
0133           if (not equal(first[i], second[i]))
0134             return false;
0135         return true;
0136       }
0137     };
0138 
0139     template <typename T, std::size_t N>
0140     struct access<std::array<T, N>> {
0141       static bool equal_(const std::array<T, N>& first, const std::array<T, N>& second) {
0142         for (std::size_t i = 0; i < N; ++i)
0143           if (not equal(first[i], second[i]))
0144             return false;
0145         return true;
0146       }
0147     };
0148 
0149 #define equal_sequence(TYPE)                                                                                           \
0150   template <typename T>                                                                                                \
0151   struct access<TYPE<T>> {                                                                                             \
0152     static bool equal_(const TYPE<T>& first, const TYPE<T>& second) {                                                  \
0153       return first.size() == second.size() &&                                                                          \
0154              std::equal(first.cbegin(),                                                                                \
0155                         first.cend(),                                                                                  \
0156                         second.cbegin(),                                                                               \
0157                         [](decltype(*first.cbegin()) a, decltype(*first.cbegin()) b) -> bool { return equal(a, b); }); \
0158     }                                                                                                                  \
0159   };
0160 
0161     equal_sequence(std::vector);
0162     equal_sequence(std::deque);
0163     equal_sequence(std::list);
0164     equal_sequence(std::set);       // ordered
0165     equal_sequence(std::multiset);  // ordered
0166 #undef equal_sequence
0167 
0168     // forward_list is a sequence, but does not provide size() and we are not yet
0169     // in C++14 so we cannot use the 4 iterators version of std::equal()
0170     template <typename T>
0171     struct access<std::forward_list<T>> {
0172       static bool equal_(const std::forward_list<T>& first, const std::forward_list<T>& second) {
0173         auto first_it = first.cbegin();
0174         auto second_it = second.cbegin();
0175 
0176         while (first_it != first.cend() and second_it != second.cend()) {
0177           if (not equal(*first_it, *second_it))
0178             return false;
0179           first_it++;
0180           second_it++;
0181         }
0182 
0183         return first_it == first.cend() and second_it == second.cend();
0184       }
0185     };
0186 
0187 // map is ordered too, we can iterate like a sequence
0188 #define equal_mapping(TYPE)                                                                                            \
0189   template <typename T, typename U>                                                                                    \
0190   struct access<TYPE<T, U>> {                                                                                          \
0191     static bool equal_(const TYPE<T, U>& first, const TYPE<T, U>& second) {                                            \
0192       return first.size() == second.size() &&                                                                          \
0193              std::equal(first.cbegin(),                                                                                \
0194                         first.cend(),                                                                                  \
0195                         second.cbegin(),                                                                               \
0196                         [](decltype(*first.cbegin()) a, decltype(*first.cbegin()) b) -> bool { return equal(a, b); }); \
0197     }                                                                                                                  \
0198   };
0199 
0200     equal_mapping(std::map);
0201 #undef equal_mapping
0202 
0203 #define equal_unorderedmapping(TYPE)                                        \
0204   template <typename T, typename U>                                         \
0205   struct access<TYPE<T, U>> {                                               \
0206     static bool equal_(const TYPE<T, U>& first, const TYPE<T, U>& second) { \
0207       if (first.size() != second.size())                                    \
0208         return false;                                                       \
0209                                                                             \
0210       auto first_it = first.cbegin();                                       \
0211       while (first_it != first.cend()) {                                    \
0212         auto second_it = second.find(first_it->first);                      \
0213         if (second_it == second.cend())                                     \
0214           return false;                                                     \
0215         if (not equal(first_it->second, second_it->second))                 \
0216           return false;                                                     \
0217         first_it++;                                                         \
0218       }                                                                     \
0219       return true;                                                          \
0220     }                                                                       \
0221   };
0222 
0223     equal_unorderedmapping(std::unordered_map);
0224 #undef equal_unorderedmapping
0225 
0226   }  // namespace serialization
0227 }  // namespace cond