Upgrade: simplify comparisons

Now able to remove most complicated comparison operators and most usages of boost::operators...
In most cases it is sufficient just to define one ''spaceship operator'',
and often even that one can be synthesised.

However — we still use boost::operators for arithmetic types,
notably the `lib::time::TimeValue`, which is addable and mutipliable
This commit is contained in:
Fischlurch 2025-07-03 23:48:08 +02:00
parent bad4827b34
commit b6a39fa831
16 changed files with 507 additions and 169 deletions

View file

@ -21,7 +21,7 @@
**
** This header is intended to be incorporated as part of the advice system implementation (advice.cpp).
** It is \em not usable as an external interface. But it is written in a rather self-contained manner,
** in order to be testable in isolation. To this end, the actual PointOfAdvice entities being organised
** in order to be testable in isolation. To this end, the actual PointOfAdvice entities \a POA organised
** by this index datastructure remain abstract (defined as template parameter), and are only manipulated
** through the following functions:
** - \c hash_value(POA)
@ -87,7 +87,6 @@
#include "lib/util.hpp"
#include "common/advice/binding.hpp"
#include <boost/operators.hpp>
#include <unordered_map>
#include <string>
@ -122,6 +121,7 @@ namespace advice {
* by invoking the \c setSolution() function on the
* corresponding PointOfAdvice entity.
*
* @tparam POA _point-of-advice_ exposing a matcher and solution
* @note element \em identity is defined in terms of pointing
* to the same memory location of a POA (point of advice).
* Thus e.g. #hasProvision means this index holds an entry
@ -141,12 +141,8 @@ namespace advice {
class Index
{
struct Entry
: pair<Binding::Matcher, POA*>
, boost::equality_comparable<Entry, POA,
boost::equality_comparable<Entry>
>
{
explicit
Entry (POA& elm)

View file

@ -76,9 +76,9 @@
#include "lib/util.hpp"
#include <boost/lexical_cast.hpp>
#include <boost/operators.hpp>
#include <memory>
#include <typeinfo>
#include <compare>
#include <memory>
#include <cctype>
#include <string>
@ -136,6 +136,8 @@ namespace lumiera {
: kind(k)
, type(t)
{ }
auto operator<=> (QueryID const&) const =default;
};
QueryID const&
@ -185,26 +187,6 @@ namespace lumiera {
};
inline bool
operator< (Goal::QueryID const& id1, Goal::QueryID const& id2)
{
return id1.kind < id2.kind
or(id1.kind == id2.kind and id1.type < id2.type);
}
inline bool
operator== (Goal::QueryID const& id1, Goal::QueryID const& id2)
{
return id1.kind == id2.kind
and id1.type == id2.type;
}
inline bool
operator!= (Goal::QueryID const& id1, Goal::QueryID const& id2)
{
return not (id1 == id2);
}
namespace {
@ -386,7 +368,6 @@ namespace lumiera {
* Implicitly convertible to and from Query instances.
*/
class QueryKey
: boost::totally_ordered<QueryKey>
{
Goal::QueryID id_;
lib::QueryText def_;
@ -442,21 +423,19 @@ namespace lumiera {
}
friend bool
operator< (QueryKey const& q1, QueryKey const& q2)
friend std::strong_ordering
operator<=> (QueryKey const& q1, QueryKey const& q2)
{
uint d1 = q1.degree();
uint d2 = q2.degree();
return d1 < d2
or(d1 == d2 and ( q1.def_ < q2.def_
or (q1.def_ == q2.def_ and q1.id_ < q2.id_)));
}
friend bool
operator== (QueryKey const& q1, QueryKey const& q2)
{
return q1.def_ == q2.def_;
if (auto o1 = d1 <=> d2; o1 != 0)
return o1;
if (auto o2 = q1.def_ <=> q2.def_; o2 != 0)
return o2;
else
return q1.id_ <=> q2.id_;
}
bool operator== (QueryKey const&) const =default;
friend size_t
hash_value (QueryKey const& q)

View file

@ -24,6 +24,8 @@
** @deprecated 10/2024 seems very likely that similar functionality moves down
** into the render-engine implementation and will no longer be considered
** a constituent of the public interface.
** @todo 6/2025 basically everything here is unused or will likely be done
** in a different way — expect this and rendergraph.cpp to be obsolete
*/
@ -31,7 +33,8 @@
#define LUMIERA_FRAMEID_H
#include <boost/operators.hpp>
#include "lib/integral.hpp"
#include <compare>
namespace lumiera {
@ -73,15 +76,14 @@ namespace lumiera {
* later on define what is actually needed; this header should then
* be replaced by a combined C/C++ header
*/
class FrameID : boost::totally_ordered<FrameID> ////////////TODO it seems we don't need total ordering, only comparison. Clarify this!
class FrameID
{
long dummy;
public:
FrameID(long dum=0) : dummy(dum) {}
operator long () { return dummy; }
bool operator< (const FrameID& other) const { return dummy < other.dummy; }
bool operator== (const FrameID& other) const { return dummy == other.dummy; }
auto operator<=> (FrameID const&) const =default;
};

View file

@ -47,7 +47,7 @@
#include "lib/util.hpp"
#include <boost/functional/hash.hpp>
#include <boost/operators.hpp>
#include <compare>
#include <string>
@ -131,7 +131,6 @@ namespace idi {
* for building a combined hash and symbolic ID.
*/
class BareEntryID
: public boost::equality_comparable<BareEntryID>
{
string symbol_;
@ -217,7 +216,6 @@ namespace idi {
template<class TY>
struct EntryID
: BareEntryID
, boost::totally_ordered< EntryID<TY> >
{
/** case-1: auto generated symbolic ID */
@ -272,15 +270,17 @@ namespace idi {
explicit
operator string() const;
friend bool operator< (EntryID const& i1, EntryID const& i2) { return i1.getSym() < i2.getSym(); }
friend auto operator<=> (EntryID const& i1, EntryID const& i2) { return i1.getSym() <=> i2.getSym(); }
};
inline bool
operator== (BareEntryID const& i1, BareEntryID const& i2)
{
return i1.getHash() == i2.getHash();
}
// Note: since we allow comparison only between EntryIDs of same type
// and also feed-down the symbol into the hash value, both equality
// and (total) ordering mesh up perfectly.

View file

@ -77,6 +77,7 @@ namespace lib {
return definition_;
}
auto operator<=> (QueryText const&) const =default;
bool
empty() const
@ -105,19 +106,6 @@ namespace lib {
private:
string normalise (string const& rawDefinition);
friend bool
operator== (QueryText const& q1, QueryText const& q2)
{
return q1.definition_ == q2.definition_;
}
friend bool
operator< (QueryText const& q1, QueryText const& q2)
{
return q1.definition_ < q2.definition_;
}
friend HashVal hash_value (QueryText const& entry);
};

View file

@ -60,7 +60,6 @@
#include "lib/symbol.hpp"
#include "lib/util.hpp"
#include <boost/operators.hpp>
#include <boost/lexical_cast.hpp>
#include <functional>
#include <string>
@ -215,7 +214,6 @@ namespace time {
, class FMT = digxel::Formatter<NUM>
>
class Digxel
: public boost::totally_ordered<Digxel<NUM,FMT>>
{
mutable
FMT buffer_;
@ -297,8 +295,8 @@ namespace time {
NUM operator++ (int) { NUM p(value_); *this =p+1; return p;}
NUM operator-- (int) { NUM p(value_); *this =p-1; return p;}
//---Supporting-totally_ordered---------
bool operator< (Digxel const& o) const { return value_ < NUM(o); }
//---Supporting-total-ordering----------
auto operator<=>(Digxel const& o) const { return value_ <=> NUM(o); }
bool operator== (Digxel const& o) const { return value_ == NUM(o); }
};

View file

@ -62,7 +62,6 @@
#include "lib/time/timecode.hpp"
#include "lib/symbol.hpp"
//#include <boost/operators.hpp>
#include <string>

View file

@ -99,6 +99,7 @@
#include <boost/operators.hpp>
#include <boost/rational.hpp>
#include <compare>
#include <cstdlib>
#include <string>
@ -139,8 +140,6 @@ namespace time {
* @see TimeVar when full arithmetics are required
*/
class TimeValue
: boost::totally_ordered<TimeValue,
boost::totally_ordered<TimeValue, raw_time_64>>
{
protected:
/** the raw (internal) time value
@ -196,12 +195,12 @@ namespace time {
/** @return is in-domain, not a boundary value */
bool isRegular() const;
// Supporting totally_ordered
friend bool operator< (TimeValue const& t1, TimeValue const& t2) { return t1.t_ < t2.t_; }
friend bool operator< (TimeValue const& t1, raw_time_64 t2) { return t1.t_ < t2 ; }
friend bool operator> (TimeValue const& t1, raw_time_64 t2) { return t1.t_ > t2 ; }
friend bool operator== (TimeValue const& t1, TimeValue const& t2) { return t1.t_ == t2.t_; }
friend bool operator== (TimeValue const& t1, raw_time_64 t2) { return t1.t_ == t2 ; }
// Supporting strong total ordering
std::strong_ordering operator<=> (TimeValue const&) const =default;
bool operator== (TimeValue const&) const =default;
std::strong_ordering operator<=> (raw_time_64 tt) const { return t_ <=> tt; }
bool operator== (raw_time_64 tt) const { return t_ == tt; }
};
@ -582,7 +581,6 @@ namespace time {
*/
class TimeSpan
: public Time
, boost::totally_ordered<TimeSpan>
{
Duration dur_;
@ -651,10 +649,15 @@ namespace time {
/** @internal diagnostics */
explicit operator std::string() const;
/// Supporting extended total order, based on start and interval length
friend bool operator== (TimeSpan const& t1, TimeSpan const& t2) { return t1.t_==t2.t_ && t1.dur_==t2.dur_; }
friend bool operator< (TimeSpan const& t1, TimeSpan const& t2) { return t1.t_< t2.t_ ||
(t1.t_==t2.t_ && t1.dur_< t2.dur_);}
/// Supporting extended strong total ordering, based on start and interval length
std::strong_ordering
operator<=> (TimeSpan const& ts) const
{
auto ord{ t_ <=> ts.t_ };
return ord != 0? ord
: dur_ <=> ts.dur_;
}
bool operator== (TimeSpan const& ts) const =default;
};

View file

@ -54,10 +54,8 @@
#include "lib/hash-value.h"
#include "lib/p.hpp"
#include <boost/type_traits/is_base_of.hpp>
#include <boost/operators.hpp>
#include <cstddef>
#include <type_traits>
#include <compare>
#include <string>
#include <vector>
#include <set>
@ -137,8 +135,7 @@ namespace asset {
* @author Ichthyo
*/
class Asset
: public boost::totally_ordered1< Asset
, util::NonCopyable >
: util::NonCopyable
{
public:
@ -147,7 +144,6 @@ namespace asset {
* sufficiently identifying any given Asset.
*/
struct Ident
: boost::totally_ordered<Ident>
{
/** element ID, comprehensible but sanitised.
* The tuple (category, name, org) is unique.
@ -181,11 +177,13 @@ namespace asset {
,const uint ver=1);
int compare (Ident const& other) const;
/** ordering of Assets is based on the ordering of Ident tuples,
* descending from Category to origin and finally to the asset element ID.
* @note version info is ignored for this comparison */
auto operator<=>(Ident const& oi) const { return std::tie ( category, org, name)
<=> std::tie (oi.category,oi.org,oi.name); }
bool operator== (Ident const& oi) const { return 0 == *this <=> oi; }
/** @note equality ignores version differences */
bool operator== (Ident const& oi) const { return compare (oi) ==0; }
bool operator< (Ident const& oi) const { return compare (oi) < 0; }
operator string () const;
@ -202,7 +200,7 @@ namespace asset {
bool operator== (Asset const& oa) const { return ident == oa.ident; }
bool operator< (Asset const& oa) const { return ident < oa.ident; }
auto operator<=>(Asset const& oa) const { return ident <=> oa.ident; }
virtual operator string () const;
@ -300,24 +298,6 @@ namespace asset {
/* ====== ordering of Assets and Asset-Pointers ====== */
/** ordering of Assets is based on the ordering
* of Ident tuples, which are supposed to be unique.
* By using our customised lumiera::P as smart ptr,
* comparison on P<Asset> ptrs will be automatically
* forwarded to the Asset comparison operators.
* @note version info is irrelevant */
inline int
Asset::Ident::compare (Asset::Ident const& oi) const
{
int res;
if (0 != (res=category.compare (oi.category))) return res;
if (0 != (res=org.compare (oi.org))) return res;
return name.compare (oi.name);
}
/** promote subtype-ptr to PAsset, e.g. for comparing */
template<class A>
inline const PcAsset
@ -329,11 +309,11 @@ namespace asset {
/** type trait for detecting a shared-ptr-to-asset */
template <class X>
struct is_pAsset : boost::false_type {};
struct is_pAsset : std::false_type { };
template <class A>
struct is_pAsset<shared_ptr<A>>
: boost::is_base_of<Asset, A> {};
: std::is_base_of<Asset, A> { };
/** marker constant denoting a NIL asset */

View file

@ -24,6 +24,7 @@
#include "lib/hash-standard.hpp"
#include <string>
#include <compare>
#include <boost/functional/hash.hpp>
@ -73,29 +74,15 @@ namespace asset {
Category (const Kind root, Literal subfolder ="")
: kind_(root), path_(subfolder) {};
bool operator== (Category const& other) const { return kind_== other.kind_ and path_== other.path_; }
bool operator!= (Category const& other) const { return kind_!= other.kind_ or path_!= other.path_; }
auto operator<=> (Category const&) const =default;
bool hasKind (Kind refKind) const { return kind_ == refKind; }
bool isWithin (Category const&) const;
void setPath (string const& newpath) { this->path_ = newpath; }
operator string () const;
friend size_t hash_value (Category const&);
int
compare (Category const& co) const
{
int res = int(kind_) - int(co.kind_);
if (0 != res)
return res;
else
return path_.compare (co.path_);
}
};

View file

@ -45,7 +45,6 @@
//#include "lib/util.hpp"
#include "lib/symbol.hpp"
//#include <boost/operators.hpp>
#include <memory>
namespace lumiera{ ///////TODO: shouldn't that be namespace lib? or steam?

View file

@ -38,7 +38,6 @@
#include "lib/format-obj.hpp"
#include "lib/util.hpp"
#include <boost/operators.hpp>
#include <functional>
#include <string>
@ -47,7 +46,6 @@ namespace steam {
namespace control {
namespace err = lumiera::error;
using boost::equality_comparable;
using lib::meta::func::bindLast;
using lib::meta::func::chained;
using lib::meta::equals_safeInvoke;
@ -77,7 +75,6 @@ namespace control {
*/
template<typename SIG, typename MEM>
class MementoTie
: public equality_comparable<MementoTie<SIG,MEM>>
{
typedef typename CommandSignature<SIG,MEM>::CaptureSig SIG_cap;
typedef typename CommandSignature<SIG,MEM>::UndoOp_Sig SIG_undo;
@ -195,7 +192,7 @@ namespace control {
template<typename SIG, typename MEM>
MementoTie<SIG,MEM>::operator std::string() const
{
if (!undo_ or !capture_)
if (not undo_ or not capture_)
return "·noUNDO·";
if (not isCaptured_)

View file

@ -49,7 +49,7 @@
#include "steam/asset/pipe.hpp"
#include "common/query.hpp"
#include <boost/operators.hpp>
#include <compare>
#include <map>
@ -121,7 +121,7 @@ namespace mobject {
class OutputMapping
: public DEF
{
typedef _def<DEF> Setup;
using Setup = _def<DEF>;
using PId = asset::ID<asset::Pipe>;
using PPipe = asset::PPipe;
@ -148,10 +148,11 @@ namespace mobject {
* of the specific resolution functor, embedded in the definition context `DEF`,
* which was given when instantiating the OutputMapping template.
* @note depends on the template parameter of the enclosing OutputMapping type!
* @remarks
* - final mapping result can be compared to Target
* - Resolvers (mapping values) can be compared based on the Pipe-hash
*/
class Resolver
: public boost::equality_comparable<Resolver, Target // final mapping result can be compared to Target...
, boost::equality_comparable<Resolver>> // mapping values can be compared.
{
OutputMapping& thisMapping_;
HashVal& pID_;

View file

@ -100,25 +100,25 @@ namespace test {
Category c5 (STRUCT);
Category c6 (META);
CHECK (0 > c1.compare(c2));
CHECK (0 > c2.compare(c3));
CHECK (0 > c3.compare(c4));
CHECK (0 > c4.compare(c5));
CHECK (0 > c5.compare(c6));
CHECK (0 > c1 <=> c2 );
CHECK (0 > c2 <=> c3 );
CHECK (0 > c3 <=> c4 );
CHECK (0 > c4 <=> c5 );
CHECK (0 > c5 <=> c6 );
CHECK (0 ==