diff --git a/admin/scons/Setup.py b/admin/scons/Setup.py index fbed43a70..32c7a5bcf 100644 --- a/admin/scons/Setup.py +++ b/admin/scons/Setup.py @@ -65,7 +65,7 @@ def defineBuildEnvironment(): env.Replace( CPPPATH =["#src"] # used to find includes, "#" means always absolute to build-root , CPPDEFINES=['LUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines , CCFLAGS='-Wall -Wextra -Wformat-security' - , CXXFLAGS='-std=gnu++17 -Wno-enum-compare' + , CXXFLAGS='-std=gnu++23 -Wno-enum-compare' , CFLAGS='-std=gnu99' ) env.Append(LINKFLAGS='-Wl,--no-undefined') # require every dependency is given on link, in the right order diff --git a/src/common/guifacade.cpp b/src/common/guifacade.cpp index 5c0e5978b..4f43ecfee 100644 --- a/src/common/guifacade.cpp +++ b/src/common/guifacade.cpp @@ -115,7 +115,7 @@ namespace stage { facade.reset ( // trigger loading of the GuiStarterPlugin... new GuiRunner ( - [=] (string* problemMessage) + [=,this] (string* problemMessage) { // will be invoked when the UI thread exits closeGuiModule(); termNotification(problemMessage); diff --git a/src/lib/allocation-cluster.hpp b/src/lib/allocation-cluster.hpp index c15a72947..067935909 100644 --- a/src/lib/allocation-cluster.hpp +++ b/src/lib/allocation-cluster.hpp @@ -121,8 +121,8 @@ namespace lib { ~AllocationCluster () noexcept; /** hard wired size of storage extents */ - static size_t constexpr EXTENT_SIZ = 256; - static size_t constexpr max_size(); + static constexpr size_t EXTENT_SIZ = 256; + static constexpr size_t max_size(); /* === diagnostics === */ diff --git a/src/lib/depend-inject.hpp b/src/lib/depend-inject.hpp index a13d243b0..72bf9f068 100644 --- a/src/lib/depend-inject.hpp +++ b/src/lib/depend-inject.hpp @@ -293,17 +293,17 @@ namespace lib { template explicit - Local (FUN&& buildInstance) + Local (FUN&& _buildInstance) { __assert_compatible(); __assert_compatible::Subclass>(); temporarilyInstallAlternateFactory (origInstance_, origFactory_ - ,[=]() - { - mock_.reset (buildInstance()); - return mock_.get(); - }); + ,[this, buildInstance=forward (_buildInstance)] + { + mock_.reset (buildInstance()); + return mock_.get(); + }); } ~Local() { diff --git a/src/lib/meta/tuple-closure.hpp b/src/lib/meta/tuple-closure.hpp index ec7dbe2f6..585c90b60 100644 --- a/src/lib/meta/tuple-closure.hpp +++ b/src/lib/meta/tuple-closure.hpp @@ -44,6 +44,7 @@ #define LIB_META_TUPLE_CLOSURE_H #include "lib/meta/function-closure.hpp" +#include "lib/meta/tuple-helper.hpp" #include "lib/util.hpp" #include @@ -140,7 +141,7 @@ namespace meta{ auto operator() (RemainingParams remPar) const { - return std::apply (unConst(this)->partialClosure_, remPar); + return apply (unConst(this)->partialClosure_, remPar); }; CLO partialClosure_; diff --git a/src/lib/meta/tuple-helper.hpp b/src/lib/meta/tuple-helper.hpp index 8829635b0..ead2c635b 100644 --- a/src/lib/meta/tuple-helper.hpp +++ b/src/lib/meta/tuple-helper.hpp @@ -42,6 +42,7 @@ #include #include +#include namespace util { // forward declaration @@ -78,6 +79,56 @@ namespace meta { using disable_if_Tuple = lib::meta::disable_if>>; + + namespace { // apply to tuple-like : helpers... + + /** @internal invocation helper similar to C++17 — but preferring a custom `get` impl */ + template + constexpr decltype(auto) + __unpack_and_apply (FUN&& f, TUP&& tup, std::index_sequence) + { // ▽▽▽ ADL + using std::get; + return std::invoke (std::forward (f) + ,get (std::forward(tup))... + ); + } + + + /** @internal invoke a metafunction with \a FUN and all element types from \a TUP */ + template class META, class FUN, class TUP> + struct _InvokeMetafunTup + { + using Tupl = std::decay_t; + using Elms = typename ElmTypes::Seq; + using Args = typename Prepend::Seq; + using Type = typename RebindVariadic::Type; + }; + + template + inline constexpr bool can_nothrow_invoke_tup = _InvokeMetafunTup::Type::value; + } + + /** + * Replacement for `std::apply` — yet applicable to _tuple-like custom types_. + * For unclear reasons, the standard chooses to reject such custom types, and + * only allows a fixed set of explicitly defined facilities from the Stdlib + * (tuple, pair, array, and some ranges stuff). + * @todo 6/2025 as a first step, this replicates the implementation from C++17; + * the second step would be to constrain this to a concept `tuple_like` + */ + template + constexpr decltype(auto) + apply (FUN&& f, TUP&& tup) noexcept (can_nothrow_invoke_tup ) + { + using Indices = std::make_index_sequence>>; + + return __unpack_and_apply (std::forward (f) + ,std::forward (tup) + ,Indices{} + ); + } + + /** * Tuple iteration: perform some arbitrary operation on each element of a tuple. * @note the given functor must be generic, since each position of the tuple @@ -87,7 +138,7 @@ namespace meta { * then employ a fold expression with the comma operator. */ template> - void + constexpr void forEach (TUP&& tuple, FUN fun) { std::apply ([&fun](auto&&... elms) @@ -112,7 +163,7 @@ namespace meta { * is used, which is guaranteed to evaluate from left to right. */ template> - auto + constexpr auto mapEach (TUP&& tuple, FUN fun) { return std::apply ([&fun](auto&&... elms) diff --git a/src/lib/p.hpp b/src/lib/p.hpp index 60d7c95d2..c82495073 100644 --- a/src/lib/p.hpp +++ b/src/lib/p.hpp @@ -31,6 +31,13 @@ ** the corresponding operators on the pointee and by allowing to specify a base class smart-ptr ** as template parameter. ** + ** @deprecated 2025 : smells like an overly zealous design, while fundamental traits of the + ** involved entities remain nebulous. Who is the »entity«? The pointer or the pointee? + ** Why do we even want to delegate relational operators, while we do not even know that + ** the target supports them? And why to we need an virtual equality? If these objects have + ** reference semantics, then a base class should compare the identity. And there should not + ** be any ordering on such elements. So the whole motivation of rolling a specialised + ** shard-ptr seems moot. /////////////////////////////////////////////////////TICKET #501 : clarify Placement and MObject identity ** @see asset.hpp ** @see custom-shared-ptr-test.cpp ** @see orderingofassetstest.cpp @@ -94,11 +101,11 @@ namespace lib { private: /* === friend operators injected into enclosing namespace for ADL === */ - //////////////////TICKET #932 Clang is unable to fill in the default template argument. Resolved in newer versions of Clang. Temporary workaround: add second parameter B + ///////////////////TICKET #932 Clang is unable to fill in the default template argument. Resolved in newer versions of Clang. Temporary workaround: add second parameter B template friend inline bool operator== (P const& p, P<_O_, B> const& q) { return (p and q)? (*p == *q) : (!p and !q); } - + ///////////////////////TICKET #501 : clarify Placement and MObject identity template friend inline bool operator!= (P const& p, P<_O_, B> const& q) { return (p and q)? (*p != *q) : !(!p and !q); } diff --git a/src/lib/random-draw.hpp b/src/lib/random-draw.hpp index 5d0fa6d98..5b263dcd3 100644 --- a/src/lib/random-draw.hpp +++ b/src/lib/random-draw.hpp @@ -234,8 +234,8 @@ namespace lib { return Tar{floor (val)}; } } //----headroom to accommodate low probabilities - static size_t constexpr QUANTISER = 1 << 4 + util::ilog2 (Tar::maxVal()-Tar::minVal()); - static double constexpr CAP_EPSILON = 1/(2.0 * QUANTISER); + static constexpr size_t QUANTISER = 1 << 4 + util::ilog2 (Tar::maxVal()-Tar::minVal()); + static constexpr double CAP_EPSILON = 1/(2.0 * QUANTISER); /** @internal draw from source of randomness */ diff --git a/src/lib/several-builder.hpp b/src/lib/several-builder.hpp index 146f94c3d..d282ca256 100644 --- a/src/lib/several-builder.hpp +++ b/src/lib/several-builder.hpp @@ -147,7 +147,7 @@ namespace lib { * elements of type \a TY in memory _with proper alignment_. */ template - size_t inline constexpr + inline constexpr size_t reqSiz() { size_t quant = alignof(TY); @@ -159,7 +159,7 @@ namespace lib { } /** determine size of a reserve buffer to place with proper alignment */ - size_t inline constexpr + inline constexpr size_t alignRes (size_t alignment) { return positiveDiff (alignment, alignof(void*)); diff --git a/src/lib/symbol.hpp b/src/lib/symbol.hpp index 1752544fe..09ebda790 100644 --- a/src/lib/symbol.hpp +++ b/src/lib/symbol.hpp @@ -148,7 +148,7 @@ namespace lib { Symbol& operator= (Symbol &&) = default; explicit operator bool() const { return not empty(); } - bool empty() const { return *this == BOTTOM or *this == EMPTY; } + bool empty() const { return *this == BOTTOM.c() or *this == EMPTY.c(); } size_t length() const diff --git a/src/lib/time/time.cpp b/src/lib/time/time.cpp index 0e2d9f9cc..1cdc82162 100644 --- a/src/lib/time/time.cpp +++ b/src/lib/time/time.cpp @@ -285,7 +285,7 @@ namespace time { Duration FrameRate::duration() const { - if (0 == *this) + if (HALTED > *this) throw error::Logic ("Impossible to quantise to an zero spaced frame grid" , error::LUMIERA_ERROR_BOTTOM_VALUE); diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 1ed1a6b4d..deeb6dd1d 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -451,28 +451,28 @@ namespace time { int HmsTC::getSecs() const { /////////////////////////////////////////////////////////////////////////////////////////////////TICKET #750 we do not want numeric accessors her — rather we want Digxel members - return (raw_time_64(tpoint_) / TIME_SCALE_sec) % 60; + return (_raw (tpoint_) / TIME_SCALE_sec) % 60; } /** @deprecated 5/25 : no numeric computations in this class! use Digxel instead! */ int HmsTC::getMins() const { - return (raw_time_64(tpoint_) / TIME_SCALE_sec / 60) % 60; + return (_raw (tpoint_) / TIME_SCALE_sec / 60) % 60; } /** @deprecated 5/25 : no numeric computations in this class! use Digxel instead! */ int HmsTC::getHours() const { - return raw_time_64(tpoint_) / TIME_SCALE_sec / 60 / 60; + return _raw (tpoint_) / TIME_SCALE_sec / 60 / 60; } /** @deprecated 5/25 : no numeric computations in this class! use Digxel instead! */ double HmsTC::getMillis() const { - return (raw_time_64(tpoint_) / TIME_SCALE_ms) % 1000; + return (_raw (tpoint_) / TIME_SCALE_ms) % 1000; } /** */ diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 31c27bd6e..d0880005b 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -191,7 +191,7 @@ namespace time { static TimeValue buildRaw_(raw_time_64); /** @internal diagnostics */ - operator std::string () const; + explicit operator std::string() const; /** @return is in-domain, not a boundary value */ bool isRegular() const; @@ -265,8 +265,6 @@ namespace time { return *this; } - /// Support mixing with plain 64bit int arithmetics - operator raw_time_64() const { return t_; } /// Support for micro-tick precise time arithmetics operator FSecs() const { return FSecs{t_, TimeValue::SCALE}; } @@ -342,7 +340,7 @@ namespace time { ); /** @internal diagnostics */ - operator std::string () const; + explicit operator std::string() const; /** convenience start for time calculations */ TimeVar operator+ (TimeValue const& tval) const { return TimeVar(*this) + tval; } @@ -405,7 +403,7 @@ namespace time { Offset stretchedByFloatFactor (double) const; /** @internal diagnostics, indicating ∆ */ - operator std::string () const; + explicit operator std::string() const; // Supporting sign flip Offset operator- () const; @@ -522,7 +520,7 @@ namespace time { /** @internal diagnostics */ - operator std::string () const; + explicit operator std::string() const; /// Supporting backwards use as offset Offset operator- () const; @@ -651,7 +649,7 @@ namespace time { void accept (Mutation const&); /** @internal diagnostics */ - operator std::string () const; + 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_; } @@ -689,7 +687,10 @@ namespace time { /** duration of one frame */ Duration duration() const; - operator std::string() const; + /** derive total ordering from base class */ + std::strong_ordering operator<=>(FrameRate const&) const = default; + + explicit operator std::string() const; }; /** convenient conversion to duration in fractional seconds */ diff --git a/src/lib/util-quant.hpp b/src/lib/util-quant.hpp index 58133ddaf..d7612c596 100644 --- a/src/lib/util-quant.hpp +++ b/src/lib/util-quant.hpp @@ -30,7 +30,7 @@ namespace util { template - inline bool constexpr + inline constexpr bool isPow2 (N n) { return n > 0 and !(n & (n-1)); diff --git a/src/lib/util.hpp b/src/lib/util.hpp index fbeba568f..fdccea057 100644 --- a/src/lib/util.hpp +++ b/src/lib/util.hpp @@ -40,9 +40,9 @@ namespace std {// forward declarations to avoid pervasive includes class set; template - IT find (IT, IT, V const&); + constexpr IT find (IT, IT, V const&); template - IT remove (IT, IT, V const&); + constexpr IT remove (IT, IT, V const&); } @@ -57,21 +57,21 @@ namespace util { template - inline int constexpr + inline constexpr int sgn (NUM n) { return (n==0)? 0 :((n<0)? -1:+1 ); } template - inline N1 constexpr + inline constexpr N1 min (N1 n1, N2 n2) { return n2 < n1? N1(n2) : n1; } template - inline N1 constexpr + inline constexpr N1 max (N1 n1, N2 n2) { return n1 < n2? N1(n2) : n1; @@ -79,7 +79,7 @@ namespace util { /** cut a numeric value to be >=0 */ template - inline NUM constexpr + inline constexpr NUM noneg (NUM val) { return (0 - inline NUM constexpr + inline constexpr NUM limited (NB lowerBound, NUM val, NB upperBound) { return min ( max (val, lowerBound) @@ -95,7 +95,7 @@ namespace util { } template - inline bool constexpr + inline constexpr bool isLimited (NB lowerBound, NUM val, NB upperBound) { return lowerBound <= val @@ -103,7 +103,7 @@ namespace util { } template - inline UN constexpr + inline constexpr UN positiveDiff (N2 newVal, UN refVal) { return UN(newVal) > refVal? UN(newVal) - refVal diff --git a/src/stage/ctrl/ui-dispatcher.hpp b/src/stage/ctrl/ui-dispatcher.hpp index 623dc8297..82adb0b38 100644 --- a/src/stage/ctrl/ui-dispatcher.hpp +++ b/src/stage/ctrl/ui-dispatcher.hpp @@ -113,8 +113,8 @@ namespace ctrl { : queue_{} , dispatcher_{} { - dispatcher_.connect( - [=]() {try { + dispatcher_.connect([=,this] + {try { queue_.invoke(); } catch (std::exception& problem) diff --git a/src/stage/notification-service.cpp b/src/stage/notification-service.cpp index b7fd70884..ef6fc2f62 100644 --- a/src/stage/notification-service.cpp +++ b/src/stage/notification-service.cpp @@ -85,7 +85,7 @@ namespace stage { void NotificationService::dispatchMsg (ID uiElement, lib::diff::GenNode&& uiMessage) { - dispatch_->event ([=]() + dispatch_->event ([=,this] { ctrl::BusTerm::mark (uiElement, uiMessage); }); @@ -136,7 +136,7 @@ namespace stage { void NotificationService::mutate (ID uiElement, MutationMessage&& diff) { - dispatch_->event ([=]() + dispatch_->event ([=,this] { // apply and consume diff message stored within closure this->change (uiElement, move(unConst(diff))); }); diff --git a/src/stage/widget/timecode-widget.cpp b/src/stage/widget/timecode-widget.cpp index 8b9fdfad9..4e6cce6e5 100644 --- a/src/stage/widget/timecode-widget.cpp +++ b/src/stage/widget/timecode-widget.cpp @@ -231,7 +231,7 @@ namespace widget { ms_seconds_ebox.set_can_focus(true); - auto connect_motion_event = [=](Gtk::EventBox& guiElm, Field fieldID) + auto connect_motion_event = [=,this](Gtk::EventBox& guiElm, Field fieldID) { auto handlerSlot = bind (mem_fun(this, &TimeCode::field_motion_notify_event), fieldID); guiElm.signal_motion_notify_event().connect (handlerSlot); @@ -249,7 +249,7 @@ namespace widget { connect_motion_event (ms_seconds_ebox, MS_Seconds); - auto connect_button_press = [=](Gtk::EventBox& guiElm, Field fieldID) + auto connect_button_press = [=,this](Gtk::EventBox& guiElm, Field fieldID) { auto handlerSlot = bind (mem_fun(this, &TimeCode::field_button_press_event), fieldID); guiElm.signal_button_press_event().connect (handlerSlot); @@ -267,7 +267,7 @@ namespace widget { connect_button_press (ms_seconds_ebox, MS_Seconds); - auto connect_button_release = [=](Gtk::EventBox& guiElm, Field fieldID) + auto connect_button_release = [=,this](Gtk::EventBox& guiElm, Field fieldID) { auto handlerSlot = bind (mem_fun(this, &TimeCode::field_button_release_event), fieldID); guiElm.signal_button_release_event().connect (handlerSlot); @@ -285,7 +285,7 @@ namespace widget { connect_button_release (ms_seconds_ebox, MS_Seconds); - auto connect_scroll_event = [=](Gtk::EventBox& guiElm, Field fieldID) + auto connect_scroll_event = [=,this](Gtk::EventBox& guiElm, Field fieldID) { auto handlerSlot = bind (mem_fun(this, &TimeCode::field_button_scroll_event), fieldID); guiElm.signal_scroll_event().connect (handlerSlot); @@ -303,7 +303,7 @@ namespace widget { connect_scroll_event (ms_seconds_ebox, MS_Seconds); - auto connect_key_press = [=](Gtk::EventBox& guiElm, Field fieldID) + auto connect_key_press = [=,this](Gtk::EventBox& guiElm, Field fieldID) { auto handlerSlot = bind (mem_fun(this, &TimeCode::field_key_press_event), fieldID); guiElm.signal_key_press_event().connect (handlerSlot); @@ -321,7 +321,7 @@ namespace widget { connect_key_press (ms_seconds_ebox, MS_Seconds); - auto connect_key_release = [=](Gtk::EventBox& guiElm, Field fieldID) + auto connect_key_release = [=,this](Gtk::EventBox& guiElm, Field fieldID) { auto handlerSlot = bind (mem_fun(this, &TimeCode::field_key_release_event), fieldID); guiElm.signal_key_release_event().connect (handlerSlot); @@ -339,7 +339,7 @@ namespace widget { connect_key_release (ms_seconds_ebox, MS_Seconds); - auto connect_focus_gain = [=](Gtk::EventBox& guiElm, Field fieldID) + auto connect_focus_gain = [=,this](Gtk::EventBox& guiElm, Field fieldID) { auto handlerSlot = bind (mem_fun(this, &TimeCode::field_focus_gain_event), fieldID); guiElm.signal_focus_in_event().connect (handlerSlot); @@ -357,7 +357,7 @@ namespace widget { connect_focus_gain (ms_seconds_ebox, MS_Seconds); - auto connect_focus_loss = [=](Gtk::EventBox& guiElm, Field fieldID) + auto connect_focus_loss = [=,this](Gtk::EventBox& guiElm, Field fieldID) { auto handlerSlot = bind (mem_fun(this, &TimeCode::field_focus_loss_event), fieldID); guiElm.signal_focus_out_event().connect (handlerSlot); @@ -1108,7 +1108,7 @@ namespace widget { if (frames != 0 && frames * drag_accum < (_raw(current_time()))) { // minus because up is negative in computer-land - pos = TimeValue (floor (pos - drag_accum * frames)); + pos = TimeValue (floor (_raw(pos) - drag_accum * frames)); set (pos, false); } else diff --git a/src/stage/workspace/dock-area.cpp b/src/stage/workspace/dock-area.cpp index 35ebc3752..644170d1c 100644 --- a/src/stage/workspace/dock-area.cpp +++ b/src/stage/workspace/dock-area.cpp @@ -136,7 +136,7 @@ namespace workspace { bool DockArea::hasPanel (const int description_index) { - return util::has_any (panels_, [=](panel::Panel* panel) + return util::has_any (panels_, [&](panel::Panel* panel) { return getPanelType(panel) == description_index; }); diff --git a/src/stage/workspace/panel-manager.cpp b/src/stage/workspace/panel-manager.cpp index d8dce0995..3aff21bcd 100644 --- a/src/stage/workspace/panel-manager.cpp +++ b/src/stage/workspace/panel-manager.cpp @@ -131,7 +131,7 @@ namespace workspace { bool PanelManager::hasPanel (const int description_index) { - return util::has_any (panels_, [=](panel::Panel* panel) + return util::has_any (panels_, [&](panel::Panel* panel) { return getPanelType(panel) == description_index; }); diff --git a/src/steam/control/steam-dispatcher.cpp b/src/steam/control/steam-dispatcher.cpp index d7c951331..afe64463b 100644 --- a/src/steam/control/steam-dispatcher.cpp +++ b/src/steam/control/steam-dispatcher.cpp @@ -356,7 +356,7 @@ namespace control { runningLoop_ = make_unique( - [=](string* problemIndicator) + [=,this](string* problemIndicator) { // when the Session thread ends.... SteamDispatcher::endRunningLoopState(); termNotification (problemIndicator); diff --git a/src/steam/engine/param-weaving-pattern.hpp b/src/steam/engine/param-weaving-pattern.hpp index 24c68f5df..59da09c66 100644 --- a/src/steam/engine/param-weaving-pattern.hpp +++ b/src/steam/engine/param-weaving-pattern.hpp @@ -307,6 +307,8 @@ namespace engine { { builder.emplaceParamDataBlock (&block(), turnoutSys); } + ///////////////OOO + Feed()= default; }; diff --git a/src/steam/mobject/mobject.hpp b/src/steam/mobject/mobject.hpp index 9411b8335..1390ef635 100644 --- a/src/steam/mobject/mobject.hpp +++ b/src/steam/mobject/mobject.hpp @@ -37,7 +37,6 @@ #include "lib/p.hpp" #include "lib/time/timevalue.hpp" -#include #include @@ -69,8 +68,7 @@ namespace mobject { */ class MObject : public Buildable, - util::NonCopyable, - boost::equality_comparable< MObject > + util::NonCopyable { protected: typedef lib::time::Duration Duration; @@ -97,12 +95,21 @@ namespace mobject { /** MObject self-test (usable for asserting) */ virtual bool isValid() const =0; - virtual Duration& getLength() =0; ////////////////////TICKET #448 - - virtual bool operator== (const MObject& oo) const =0; ///< needed for handling by lumiera::P + virtual Duration& getLength() =0; ///////////////////////////TICKET #448 + + /** needed for handling by lumiera::P + * @deprecated 2025 this seems not well motivated. lumiera::P was created + * to support comparisons, and now we do not know how to implement them, + * and thus we make the operation virtual. Are MObjects conceived as + * having value semantics? For reference semantics, comparing the + * »identity« should be sufficient + * @note 2025 changed to a predicate to cope with C++20 reversed operators. + */ + virtual bool isEquivalentTo (const MObject& oo) const =0; ////////////////////////////////////////TICKET #501 : clarify Placement and MObject identity + + friend bool operator== (MObject const& o1, MObject const& o2) { return o1.isEquivalentTo(o2); } protected: - virtual string initShortID() const =0; }; diff --git a/src/steam/mobject/session/abstractmo.cpp b/src/steam/mobject/session/abstractmo.cpp index 3add0d33e..48db35011 100644 --- a/src/steam/mobject/session/abstractmo.cpp +++ b/src/steam/mobject/session/abstractmo.cpp @@ -31,9 +31,10 @@ namespace session { /** default/fallback implementation of equality * using literal object identity (same address). * Required to enable handling by lumiera::P + * @deprecated 2025, see comment in MObject ////////////////////////////////////////////////////////////TICKET #501 : clarify Placement and MObject identity */ bool - AbstractMO::operator== (const MObject& oo) const + AbstractMO::isEquivalentTo (const MObject& oo) const { return (this == &oo); } diff --git a/src/steam/mobject/session/abstractmo.hpp b/src/steam/mobject/session/abstractmo.hpp index 26ab0aecf..0f9f69041 100644 --- a/src/steam/mobject/session/abstractmo.hpp +++ b/src/steam/mobject/session/abstractmo.hpp @@ -59,7 +59,7 @@ namespace session { return length_; } - bool operator== (const MObject& oo) const; + bool isEquivalentTo (const MObject& oo) const override; protected: void diff --git a/src/steam/play/output-director.cpp b/src/steam/play/output-director.cpp index 10321f833..c5f9cf940 100644 --- a/src/steam/play/output-director.cpp +++ b/src/steam/play/output-director.cpp @@ -94,9 +94,9 @@ namespace play { { shutdown_initiated_ = true; launchDetached ("Output shutdown supervisor" - ,[=]{ - bringDown (completedSignal); - }); + ,[=,this]{ + bringDown (completedSignal); + }); } } diff --git a/src/vault/gear/engine-observer.hpp b/src/vault/gear/engine-observer.hpp index 001933ee6..01dee4a61 100644 --- a/src/vault/gear/engine-observer.hpp +++ b/src/vault/gear/engine-observer.hpp @@ -59,7 +59,7 @@ namespace gear { class EngineEvent { protected: - static size_t constexpr RAW_SIZ = 3; + static constexpr size_t RAW_SIZ = 3; using Storage = std::array; template diff --git a/src/vault/gear/scheduler-commutator.hpp b/src/vault/gear/scheduler-commutator.hpp index 7ce806cbf..854a4e242 100644 --- a/src/vault/gear/scheduler-commutator.hpp +++ b/src/vault/gear/scheduler-commutator.hpp @@ -82,9 +82,7 @@ namespace gear { using lib::time::FSecs; using lib::time::Time; using std::atomic; - using std::memory_order::memory_order_relaxed; - using std::memory_order::memory_order_acquire; - using std::memory_order::memory_order_release; + using std::memory_order; using std::chrono_literals::operator ""us; using std::chrono::microseconds; @@ -129,8 +127,8 @@ namespace gear { { ThreadID expect_noThread; // expect no one else to be in... return groomingToken_.compare_exchange_strong (expect_noThread, thisThread() - ,memory_order_acquire // success also constitutes an acquire barrier - ,memory_order_relaxed // failure has no synchronisation ramifications + ,memory_order::acquire // success also constitutes an acquire barrier + ,memory_order::relaxed // failure has no synchronisation ramifications ); } @@ -145,7 +143,7 @@ namespace gear { { // expect that this thread actually holds the Grooming-Token REQUIRE (groomingToken_.load(memory_order_relaxed) == thisThread()); const ThreadID noThreadHoldsIt; - groomingToken_.store (noThreadHoldsIt, memory_order_release); + groomingToken_.store (noThreadHoldsIt, memory_order::release); } /** diff --git a/src/vault/gear/work-force.cpp b/src/vault/gear/work-force.cpp index 433919f46..67e4c11fb 100644 --- a/src/vault/gear/work-force.cpp +++ b/src/vault/gear/work-force.cpp @@ -66,7 +66,7 @@ namespace gear { work::performRandomisedSpin (size_t stepping, size_t randFact) { size_t degree = CONTEND_SOFT_FACTOR * (1+randFact) * stepping; - for (volatile size_t i=0; igridLocal(TimeValue(testPoint)); - return int(quantised); + return _raw(quantised); } }; diff --git a/tests/basics/time/time-basics-test.cpp b/tests/basics/time/time-basics-test.cpp index 7d22aed32..f879a6bcd 100644 --- a/tests/basics/time/time-basics-test.cpp +++ b/tests/basics/time/time-basics-test.cpp @@ -101,7 +101,7 @@ namespace test{ CHECK (!(var < ref) ); CHECK ( (var > ref) ); - raw_time_64 rat(var); + raw_time_64 rat = _raw(var); CHECK (!(rat == ref) ); CHECK ( (rat != ref) ); CHECK ( (rat >= ref) ); diff --git a/tests/basics/time/time-parsing-test.cpp b/tests/basics/time/time-parsing-test.cpp index ae9d459d8..ef537aee0 100644 --- a/tests/basics/time/time-parsing-test.cpp +++ b/tests/basics/time/time-parsing-test.cpp @@ -62,9 +62,9 @@ namespace test{ { TimeValue parsed = FMT::parse (timeSpec_, *grid_); CHECK (parsed == expected, "parsing '%s' resulted in %s instead of %s" - , cStr(timeSpec_) - , cStr(Time(parsed)) - , cStr(Time(expected))); + , timeSpec_.c_str() + , string{Time(parsed)}.c_str() + , string{Time(expected)}.c_str()); } void diff --git a/tests/basics/time/time-value-test.cpp b/tests/basics/time/time-value-test.cpp index 321d3f8dd..9f22a9f38 100644 --- a/tests/basics/time/time-value-test.cpp +++ b/tests/basics/time/time-value-test.cpp @@ -142,7 +142,7 @@ namespace test{ CHECK (var < Time::MAX); CHECK (var > Time::MIN); - raw_time_64 raw (var); + raw_time_64 raw = _raw(var); CHECK (raw == org); CHECK (raw > org - two); diff --git a/tests/core/steam/asset/identity-of-assets-test.cpp b/tests/core/steam/asset/identity-of-assets-test.cpp index 8a2852045..762037c5b 100644 --- a/tests/core/steam/asset/identity-of-assets-test.cpp +++ b/tests/core/steam/asset/identity-of-assets-test.cpp @@ -83,7 +83,7 @@ namespace test { AssetManager& aMang = AssetManager::instance(); CHECK (aMang.getAsset (mm1->getID()) == mm2); // record of mm1 was replaced by mm2 - CHECK (aMang.getAsset (mm2->getID()) == mm2); + CHECK (aMang.getAsset (mm2->getID()) == mm2); ////////////////////////TICKET #501 : clarify Placement and MObject identity CHECK (aMang.known (mm1->getID())); CHECK (aMang.known (mm2->getID())); diff --git a/tests/core/steam/control/command-argument-test.cpp b/tests/core/steam/control/command-argument-test.cpp index 52bedfe37..1f0b50df2 100644 --- a/tests/core/steam/control/command-argument-test.cpp +++ b/tests/core/steam/control/command-argument-test.cpp @@ -82,7 +82,7 @@ namespace test { return element_; } - operator string() const { return element_; } + operator string() const { return util::toString(element_); } friend bool diff --git a/tests/core/steam/mobject/builder/builder-tool-test.cpp b/tests/core/steam/mobject/builder/builder-tool-test.cpp index c7ac9c232..bc80d4722 100644 --- a/tests/core/steam/mobject/builder/builder-tool-test.cpp +++ b/tests/core/steam/mobject/builder/builder-tool-test.cpp @@ -68,7 +68,7 @@ namespace test { { Placement& pC = getPlacement(); cout << "Clip on media : "<< pC->getMedia() <<"\n"; - CHECK (pC->operator==(c)); + CHECK (pC->isEquivalentTo(c)); log_ = string (pC); } void treat (AbstractMO&) diff --git a/tests/library/custom-shared-ptr-test.cpp b/tests/library/custom-shared-ptr-test.cpp index 73fd1a689..269724691 100644 --- a/tests/library/custom-shared-ptr-test.cpp +++ b/tests/library/custom-shared-ptr-test.cpp @@ -22,8 +22,6 @@ #include "lib/p.hpp" -#include - namespace lib { @@ -37,15 +35,13 @@ namespace test{ struct X - : boost::totally_ordered { long x_; - X(long x=0) : x_(x) {} + explicit X(long x=0) : x_(x) {} operator long () { return x_; } - bool operator< (const X& ox) const { return x_ < ox.x_; } - bool operator== (const X& ox) const { return x_ == ox.x_; } + std::strong_ordering operator<=>(X const&) const = default; virtual ~X() {} // using RTTI }; diff --git a/tests/library/format-helper-test.cpp b/tests/library/format-helper-test.cpp index b9a380cb7..e8eb99f8b 100644 --- a/tests/library/format-helper-test.cpp +++ b/tests/library/format-helper-test.cpp @@ -27,6 +27,7 @@ #include +using lumiera::error::Fatal; using lib::transformIterator; using lib::iter_stl::snapshot; using lib::iter_stl::eachElm; @@ -53,6 +54,12 @@ namespace test { operator string() const { return "hey Joe!"; } }; + class Bomb + { + public: + operator string() const { throw Fatal{"mistake"}; } + }; + class AutoCounter @@ -110,7 +117,7 @@ namespace test { CHECK (toString (chatterer) == "hey Joe!"_expect); CHECK (toString (&chatterer) == "↗hey Joe!"_expect); // pointer indicated - CHECK (toString (nullptr) == "↯"_expect); // runtime exception, caught + CHECK (toString (Bomb{}) == "↯"_expect); // runtime exception, caught CHECK (toString (true) == "true"_expect); // special handling for bool CHECK (toString (2+2 == 5) == "false"_expect); diff --git a/tests/stage/bus-term-test.cpp b/tests/stage/bus-term-test.cpp index aa82c4166..4af5aa2bb 100644 --- a/tests/stage/bus-term-test.cpp +++ b/tests/stage/bus-term-test.cpp @@ -660,7 +660,7 @@ namespace test { */ SessionThread(function notifyGUI) : ThreadJoinable{"BusTerm_test: asynchronous diff mutation" - , [=] + , [this,notifyGUI] { uint cnt = randGen_.i(MAX_RAND_BORGS); for (uint i=0; i + + + + + + + + + + + + + + + + + +

+ das behebt in der Tat eine sonderbare Asymmetrie und ermöglicht einen wichtigen Fall: wenn man transparent auf einer Kopie von *this arbeiten möchte (was stets der Fall sein dürfte, wenn man einen λ-Dispatch in einen anderen Thread macht) +

+ +
+ +
+
+ + + + + + +
    +
  • + man muß selber eigentlich nur noch einen Spaceship-Operator implementieren +
  • +
  • + oder für rein equality-comparables: einen operator== +
  • +
  • + Boost::Operators wird damit praktisch obsolet +
  • +
+ +
+ + + + + + + + + + + + + + + + +

+ aber nur wenn es eindeutig ist; typischerweise muß dazu die Vergleichs-Kategorie mit angegeben werden. Also z.B. std::strong_ordering +

+ +
+ + +
+
+ + + +
+ + + + + + + + + +

+ früher hat man oft einen Basis-Operator in der Klasse definiert, und dann einen freistehenden gemischten Operator (z.B. mit einem Integral-Datentyp) dazu. Oder umgekehrt: in der Klasse den Vergleich mit dem Integraltyp plus eine implizite Konvertierung. Solche Konstrukte führen jetzt oft entweder zu Ambivalenzen (Compile-Fehler), oder zur Endlos-Rekursion +

+ +
+
+ + + + +

+ Lösung: Basis-Operatoren oder Spaceship direkt implementieren +

+ +
+ + +
+ + + + + +
+ + +
@@ -157191,6 +157300,15 @@ class Something + + + + + + + + + @@ -165636,6 +165754,877 @@ Since then others have made contributions, see the log for the history. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ weil *this tatsächlich von boost::rational<uint> erbt; der Check soll ja eine division-by-Zero verhindern, also *this soll nicht 0 fps sein +

+ +
+
+ + + + + + +

+ dafür gibt es eigentlich keine Entschuldigung bei Typen, die auch arithmetisch sein sollen; denn leider wird int(0) implizit in CStr promoted, und von dort weiter in Literal bzw Symbol +

+ +
+ + + +
+
+ + + + +

+ boost::rational<unsigned int>::bool_type +

+

+ sollte nicht eben dieser fehlgeleitete Vergleich durch ein save-bool vermieden werden...? +

+

+ ...wobei, das Ergebnis wäre hier auch korrekt (da 0 auch false) +

+ +
+
+ + + + + + + + + + + + +

+ wobei vor allem relevant wären die Vergleiche via "compatible integer types". +

+ +
+
+ + + + + + + + + +
+
+

+ // +

+

+ // Non-member operators: previously these were provided by Boost.Operator, but these had a number of +

+

+ // drawbacks, most notably, that in order to allow inter-operability with IntType code such as this: +

+

+ // +

+

+ // rational<int> r(3); +

+

+ // assert(r == 3.5); // compiles and passes!! +

+

+ // +

+

+ // Happens to be allowed as well :-( +

+

+ // +

+

+ // There are three possible cases for each operator: +

+

+ // 1) rational op rational. +

+

+ // 2) rational op integer +

+

+ // 3) integer op rational +

+

+ // Cases (1) and (2) are folded into the one function. +

+

+ // +

+

+ +

+
+
+ +
+
+ + + + + + + + +

+ ...und nicht (mehr) per Vererbung. +

+

+ Erläuterung: +

+

+ Die Implementierung von is_compatible_integer  kombiniert mehrere compile-Time Trait-Checks. Grundsätzlich gilt für alle Traits, daß sie nur direkt auf dem jeweiligen Template-Argument arbeiten, nicht aber eine eventuelle Basisklasse heranziehen. Das Problem ist mir bekannt; die Sprache C++ gibt das einfach nicht her...  (Kann mich an die genaue Begründung im Moment nicht erinnern, müßte irgendwo in dieser Mindmap hier stehen ... war das beim Parser?) +

+ +
+
+
+ + + + + + + + + + + + +
+
+

+ /** derive total ordering from base class */ +

+

+ std::strong_ordering operator<=>(FrameRate  const&) const =  default; +

+

+ +

+
+
+ +
+ + +
+
+
+ + + + + + + + + +

+ std::memory_order_release = std::memory_order::release +

+ +
+
+ + + + + + +
+ + + + + + + + + + + + +

+ __is_tuple_like_v<typename std::remove_cvref<_Tp>::type> [with _Tp = lib::meta::ArrayAdapt<unsigned int, unsigned int, unsigned int, unsigned int>&] +

+ +
+ +
+ + + + +

+ ...with +

+

+ with _Tuple = lib::meta::ArrayAdapt<uint, uint, uint, uint>& +

+ +
+
+ + + + + + + + + + +

+ Problem: »tuple_like« ist exposition-only +

+ +
+ +
+
+ + + + + +

+ Ärgernis-1 : das exposition-only concept »tuple_like« +

+ +
+ + + +

+ Anscheinend wollte man da bessere Fehlermeldungen und dann wurden nur die Stakeholder aus dem Standard bzw. der Stdlib berücksichtigt; der Standard ist in der Tat so formuliert, daß er eigentlich nur auf eine feste Liste von bekannten Tuple-likes abstellt. Weiß nicht ob man sich überhaupt einen Gedanken gemacht hat, ein generisches tuple_like  bereitzustellen; so wie man das Mindset der Kommittee-Mitglieder kennt, scheitert sowas daran, daß man definitiv nicht alles erfassen kann, was per Structured Binding greifbar ist. +

+ +
+
+ + + + +

+ Ärgernis-2 : die std::apply Impl.  ruft explizit std::get +

+ +
+ + + +

+ Das ist ein weiteres Problem, und bestand bereits in C++17; und es zwingt dazu, eine eigene Spezialisierung von get in den Namespace std:: einzufügen — was nun mit C++20 explizit verboten wird. Was dann bedeutet, daß die restriktive Auslegung rückwirkend als "immer schon beabsichtigt" erscheint (und ich trau mir aber wetten, daß das Problem anfänglich nicht gesehen wurde) +

+ +
+
+
+ + + + + + + + + +

+ dort könnte dann auch das Concept bereitgestellt werden, und eine Ausweitung auf diverse weitere Verwendungen (forEach etc...) +

+ +
+
+ + + + + +

+ allerdings dann korrekt für einen Erweiterungspunkt get +

+ +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ Parsing(...).should_yield() +

+ +
+ + + + + + + + +

+ hier wird die expectation als Zeitangabe gesetzt, und dann nur zur Fehlermeldung gerendert +

+ +
+
+
+
+
+
+ + + + + + + + + + + + +

+ Symbol::empty() ist direkt inline definiert +

+ +
+ + + +

+ ...eine Frage der Code-Anordnung; +

+

+ ich hab das so gemacht, weil es kompakter und lesbarer ist, die Definition diretkt in den Klassen zu sehen; das geht nur, weil dann eben hilfsweise der Vergleich mit einem CStr genommen wird, in den sich ein Literal oder Symbol implizit stets konvertieren läßt. Effektiv läuft damit ohnehin der gleich Code, nämlich ein einfacher Pointer-Vergleich. +

+ +
+
+ + + + +

+ ...der ganz regulär als freie Funktion definiert ist; und zwar verschieden für den Vergleich von zwei Literals und zwei Symbols. +

+ +
+
+ + + + +

+ ...und jetzt kommt C++20 und will hilfreich sein +

+ +
+ + + +

+ ... und merkt dabei, daß diese implizite Konvertierung auf beide Seiten anwendbar wäre +

+ +
+ +
+ + + + +

+ Sauber wäre es, diese Definition erst nach der Definition der Vergleichsoperatoren zu geben, als inline-Funktion. Denn dann würde der Compiler den direkten Vergleichsoperator sehen. Aber der menschliche Leser könnte übersehen daß Symbol::empty() sich unterscheidet von Literal::empty(). Es soll nämlich auch das Symbol{"⟂"} als »empty« gelten +

+ +
+ +
+ + + +
+
+ + + + + + +

+ grrrr... das Design von MObject ist planlos +

+ +
+ + + +

+ weiß nicht, wozu die Vergleichsoperatoren überhaupt gut sein sollen; das sieht nach Value-Semantik aus, aber andererseits hat doch ein MObject wohl eine klar definierte Identität? +

+ +
+ +
+ + + + + + + + + + + + + +

+ ...denn das zahlt sich nur bei komplexeren Relationen aus; eine Negation wird man ja wohl noch schreiben können, ohne daß ein Zacken aus der Krone bricht +

+ +
+ +
+
+
+ + + + + + + + + +

+ ...warum wird das erst jetzt mit C++20 sichtbar?? +

+

+ ......anscheined wird hier direkt der Conversion-Operator nach raw_time_64 genommen (und von dort in einen char) +

+ +
+ + + + +

+ indem man string{val} konstruiert +

+ +
+ + + + + + + + + + + + + + +

+ narrowing conversion of '(& val)->lib::time::TimeVar::operator lib::time::raw_time_64()' from 'lib::time::raw_time_64' {aka 'long int'} to 'char' +

+ +
+ + + + + + + +

+ eigentlich sollte es diesen Konversionspfad auch nicht geben +

+ +
+ + + +

+ ...erscheint mir als ein Überbleibsel von den Anfängen, wo ich dachte, TimeVar wäre eine Ausnahme, und es wäre sinnvoll, diese Ausnahme bequem zu gestalten und auch gleich noch eine Hintertür einzubauen — keine gute Idee... +

+ +
+
+ + + + +

+ also dann ⟹ weg damit (und  _raw(var) explizit verwenden) +

+ +
+ +
+
+
+
+ + + + + + +
+
+

+ src/vault/gear/work-force.cpp: In function 'void vault::gear::work::performRandomisedSpin(size_t, size_t)': +

+

+ src/vault/gear/work-force.cpp:69:43: warning: '++' expression of 'volatile'-qualified type is deprecated [-Wvolatile] +

+

+ 69 | for (volatile size_t i=0; i<degree; ++i) {/*SPIN*/} +

+

+ +

+
+
+ +
+
+ + + + +

+ Motivation war: irregeleitete Verwendungen von volatile verhindern +

+ +
+ + + +

+ Insgesamt bleibt volatile erhalten, ist aber nur noch an wenigen Stellen erlaubt, und zwingt dazu, Zugriffe explizit auszuschreiben (keine compound-assignment-Operatoren mehr). Einige dieser Einschränkungen wurden (nach Kritik aus der Embedded-community) in C++23 wieder zurückgenomm +

+ +
+
+ + + + + + + + +

+ ...weil es eine "discarded value expression" ist; wenn man die gleiche Zuweisung im Schleifenrumpf schreibt, gibt es keine Warnung (da erkennt der Compiler das) — nur in der for-expression erkennt er es nicht +

+ +
+
+ + + +
+ + + +
+
+ + + + +

+ Zeile 220: +

+

+ CHECK (5 == *pX);  // implicit conversion from X to long +

+ +
+ + + + +

+ ...von diesem speziellen shared-ptr möchte ich nur noch wegrennen... +

+ +
+ +
+ + + + + + + + + + + + + + + + + + +

+ denn nur so entsteht die 2. Variante, die für die Intention des Testsfalles hier völlig daneben liegt (unbeschadet der Tatsache, daß der Testfall selber dämlich ist, und das, was ich hier teste (den lumiear::P), schleunigst auf den Müll gehörte) +

+ +
+ +
+
+
+ + + + + + + + + + + + +

+ in beiden Fällen der gleiche Fehler, man sollte hier per Referenz capturen, da wir ohnehin direkt im Callstack bleiben +

+ +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ Digxel<long, digxel::CountFormatter>::operator long() at /Werk/devel/lumi/src/lib/time/digxel.hpp:257 0x7ffff48f62b4 +

+ +
+ +
+ + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +

+ ...denn das ist ein wichtiges Argument; speziell Literal soll die CStr eigentlich überall verdrängen können, und zwar ohne viel Zeremonie. Denn für std::string verhält es sich genauso +

+ +
+
+ + + + +

+ ...Solange es keine automatische Konvertierung gibt, die eine Brücke in ein anderes Ökosystem baut. Also (wie im Beispiel, das diese Diskussion auslöst) eine implizite Konvertierung von etwas Arrithmetischem in etwas String-artiges +

+ +
+ +
+
+ + + + + + + + + + + +

+ * @todo with C++20 the body of the implementation can be replaced by std::invoke_r //////////////////////TICKET  #1245 +

+ +
+
+ + + + +

+ hier fasse ich mit constexpr-if die zwei Fälle zusammen (void-Funktion und Rückgabewert) +

+ +
+
+
+
@@ -166735,7 +167724,7 @@ Since then others have made contributions, see the log for the history.
- +