text: #ifndef BOOST_DESCRIBE_OPERATORS_HPP_INCLUDED
  #define BOOST_DESCRIBE_OPERATORS_HPP_INCLUDED
  
  // Copyright 2020, 2021 Peter Dimov
  // Distributed under the Boost Software License, Version 1.0.
  // https://www.boost.org/LICENSE_1_0.txt
  
  #include <boost/describe/detail/config.hpp>
  
  #if defined(BOOST_DESCRIBE_CXX14)
  
  #include <boost/describe/bases.hpp>
  #include <boost/describe/members.hpp>
  #include <boost/describe/modifiers.hpp>
  #include <boost/mp11/algorithm.hpp>
  #include <type_traits>
  #include <iosfwd>
  
  namespace boost
  {
  namespace describe
  {
  
  namespace detail
  {
  
  template<class T,
      class Bd = describe_bases<T, mod_any_access>,
      class Md = describe_members<T, mod_any_access>>
  bool eq( T const& t1, T const& t2 )
  {
      bool r = true;
  
      mp11::mp_for_each<Bd>([&](auto D){
  
          using B = typename decltype(D)::type;
          r = r && (B const&)t1 == (B const&)t2;
  
      });
  
      mp11::mp_for_each<Md>([&](auto D){
  
          r = r && t1.*D.pointer == t2.*D.pointer;
  
      });
  
      return r;
  }
  
  template<class T,
      class Bd = describe_bases<T, mod_any_access>,
      class Md = describe_members<T, mod_any_access>>
  bool lt( T const& t1, T const& t2 )
  {
      int r = 0;
  
      mp11::mp_for_each<Bd>([&](auto D){
  
          using B = typename decltype(D)::type;
          if( r == 0 && (B const&)t1 < (B const&)t2 ) r = -1;
          if( r == 0 && (B const&)t2 < (B const&)t1 ) r = +1;
  
      });
  
      mp11::mp_for_each<Md>([&](auto D){
  
          if( r == 0 && t1.*D.pointer < t2.*D.pointer ) r = -1;
          if( r == 0 && t2.*D.pointer < t1.*D.pointer ) r = +1;
  
      });
  
      return r < 0;
  }
  
  template<class Os, class T,
      class Bd = describe_bases<T, mod_any_access>,
      class Md = describe_members<T, mod_any_access>>
  void print( Os& os, T const& t )
  {
      os << "{";
  
      bool first = true;
  
      mp11::mp_for_each<Bd>([&](auto D){
  
          if( !first ) { os << ", "; }
          first = false;
  
          using B = typename decltype(D)::type;
          os << (B const&)t;
  
      });
  
      mp11::mp_for_each<Md>([&](auto D){
  
          if( !first ) { os << ", "; }
          first = false;
  
          os << "." << D.name << " = " << t.*D.pointer;
  
      });
  
      os << "}";
  }
  
  } // namespace detail
  
  namespace operators
  {
  
  template<class T> std::enable_if_t<
      has_describe_bases<T>::value && has_describe_members<T>::value && !std::is_union<T>::value, bool>
      operator==( T const& t1, T const& t2 )
  {
      return detail::eq( t1, t2 );
  }
  
  template<class T> std::enable_if_t<
      has_describe_bases<T>::value && has_describe_members<T>::value && !std::is_union<T>::value, bool>
      operator!=( T const& t1, T const& t2 )
  {
      return !detail::eq( t1, t2 );
  }
  
  template<class T> std::enable_if_t<
      has_describe_bases<T>::value && has_describe_members<T>::value && !std::is_union<T>::value, bool>
      operator<( T const& t1, T const& t2 )
  {
      return detail::lt( t1, t2 );
  }
  
  template<class T> std::enable_if_t<
      has_describe_bases<T>::value && has_describe_members<T>::value && !std::is_union<T>::value, bool>
      operator>=( T const& t1, T const& t2 )
  {
      return !detail::lt( t1, t2 );
  }
  
  template<class T> std::enable_if_t<
      has_describe_bases<T>::value && has_describe_members<T>::value && !std::is_union<T>::value, bool>
      operator>( T const& t1, T const& t2 )
  {
      return detail::lt( t2, t1 );
  }
  
  template<class T> std::enable_if_t<
      has_describe_bases<T>::value && has_describe_members<T>::value && !std::is_union<T>::value, bool>
      operator<=( T const& t1, T const& t2 )
  {
      return !detail::lt( t2, t1 );
  }
  
  template<class T, class Ch, class Tr> std::enable_if_t<
      has_describe_bases<T>::value && has_describe_members<T>::value && !std::is_union<T>::value,
      std::basic_ostream<Ch, Tr>&>
      operator<<( std::basic_ostream<Ch, Tr>& os, T const& t )
  {
      os.width( 0 );
      detail::print( os, t );
      return os;
  }
  
  } // namespace operators
  
  } // namespace describe
  } // namespace boost
  
  #endif // defined(BOOST_DESCRIBE_CXX14)
  
  #endif // #ifndef BOOST_DESCRIBE_OPERATORS_HPP_INCLUDED
  