WLink: finished incl. exception handling guarantees and documentation

This commit is contained in:
Fischlurch 2018-08-16 18:22:32 +02:00
parent ae26012bf5
commit a74dc596ce
4 changed files with 91 additions and 29 deletions

View file

@ -25,7 +25,14 @@
** A smart link to an GTK widget with automatic disconnection.
** Allows to hold a connection to a [trackable] without taking ownership.
** The link is statefull, can be reconnected, and automatically transitions
** into disconnected state when the target dies.
** into disconnected state when the target dies. The link state can be tested
** by `bool` conversion. WLink objects are fully copyable, and each copy has
** its own attachment state and can be reconnected independently.
**
** The purpose of WLink is to support collaborations between controllers and
** widgets or between widgets. Whenever some logic works with or relies on
** some other UI entity -- which it might even _"allocate"_ -- without taking
** ownership, the relation can be implemented with a WLink.
**
** @warning this class is not threadsafe, because lib SigC++ is not either,
** and it can only be used reliably from within the GUI thread.
@ -52,6 +59,7 @@
#include "lib/error.hpp"
#include "lib/format-string.hpp"
#include <type_traits>
#include <sigc++/trackable.h>
@ -60,7 +68,10 @@
namespace gui {
namespace model {
using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE;
namespace error = lumiera::error;
using error::LUMIERA_ERROR_BOTTOM_VALUE;
using util::_Fmt;
/**
@ -68,6 +79,7 @@ namespace model {
* Automatically installs a callback to switch this link into detached state
* when the target (widget) is destroyed.
* @warning _not_ threadsafe
* @note only EX_SANE, since attaching, detaching and swapping might throw.
*/
template<class TAR>
class WLink
@ -81,9 +93,12 @@ namespace model {
public:
~WLink()
{
this->clear();
try {
this->clear();
}
ERROR_LOG_AND_IGNORE (gui, "Detaching managed WLink from Widget")
}
WLink()
WLink() noexcept
: widget_{nullptr}
{ }
@ -103,20 +118,28 @@ namespace model {
}
WLink&
operator= (WLink other)
operator= (WLink other) ///< @warning only EX_SANE
{
swap (*this, other);
return *this;
}
/** swap the pointees, including callback registration.
* @warning only EX_SANE.
* Might leave the following intermediary states
* - only r was detached
* - both r and l are detached
* - both detached, but only l attached to the former target of r,
* while the former target of l is now completely detached.
*/
friend void
swap (WLink& l, WLink& r)
{
TAR* tl = l.widget_;
TAR* tr = r.widget_;
if (tl == tr) return;
l.clear();
r.clear();
l.clear();
if (tr) l.widget_ = l.attachTo (*tr);
if (tl) r.widget_ = r.attachTo (*tl);
}
@ -143,22 +166,26 @@ namespace model {
}
/** detach and deactivate this link */
/** detach and deactivate this link
* @note EX_STRONG (but only under the assumption that also
* sigc::trackable::remove_destroy_notify_cllback is EX_STRONG)
*/
void
clear() noexcept
clear()
{
if (widget_)
try {
widget_->remove_destroy_notify_callback (&widget_);
}
catch(...) { }
widget_->remove_destroy_notify_callback (&widget_);
widget_ = nullptr;
}
/** (re)connect this smart link to the given target.
* Any previously existing link is detached beforehand */
* Any previously existing link is detached beforehand
* @note EX_SANE only (assuming `sigc::trackable` is sane)
* Might leave this WLink in disconnected state when throwing
* @throws error::External when registration with `sigc::trackable` fails
*/
void
connect (TAR& otherTarget) noexcept
connect (TAR& otherTarget)
{
if (widget_ == &otherTarget) return;
clear();
@ -170,15 +197,16 @@ namespace model {
__ensureAlive() const
{
if (not widget_)
throw lumiera::error::State ("zombie widget encountered"
, LERR_(BOTTOM_VALUE));
throw error::State ("zombie widget encountered"
, LERR_(BOTTOM_VALUE));
}
/** @internal installs the necessary callback
* to detach this link in case the target is destroyed
* @note EX_STRONG
*/
TAR*
attachTo (TAR& target) noexcept
attachTo (TAR& target)
{
try {
target.add_destroy_notify_callback (&widget_
@ -191,9 +219,14 @@ namespace model {
});
return &target;
}
catch(...)
catch (std::exception& problem)
{
return nullptr;
throw error::External (problem, _Fmt{"WLink could not attach to %s. Problem is %s"} % target % problem);
}
catch (...)
{
ERROR (gui, "Unknown exception while attaching WLink");
throw error::External (_Fmt{"WLink could not attach to %s due to unidentified Problems"} % target);
}
}
};

View file

@ -17,6 +17,16 @@ return: 0
END
PLANNED "low-level widget access" ElementAccess_test <<END
return: 0
END
TEST "managed link to widget" WLink_test <<END
return: 0
END
TEST "storage record for captured UI state" StateMapGroupingStorage_test <<END
return: 0
END

View file

@ -144,6 +144,7 @@ namespace test {
}
/** @test registration state is properly handled on copy, move and swap */
void
verify_copy()
{
@ -194,10 +195,19 @@ namespace test {
// l1 = uu;
// l1.connect(*uu);
// but it is a compile time check...
// But it is a compile time check...
// At runtime, only the bare pointer is managed
l1 = reinterpret_cast<WLink<Wint>&&> (lu);
CHECK ((int)uu->val == l1->val);
CHECK (not lu);
CHECK (not lu); // assignment was actually a move
// even the subversively attached link is managed properly
uu.reset();
CHECK (not l1);
// others unaffected...
CHECK (not l2);
CHECK (l3);
}
};

View file

@ -3174,13 +3174,13 @@
<node CREATED="1534336320373" ID="ID_708892482" MODIFIED="1534336324377" TEXT="Grundbausteine"/>
<node CREATED="1534336325916" ID="ID_762624766" MODIFIED="1534336335351" TEXT="Bindeglieder"/>
<node CREATED="1534336335963" ID="ID_1176525116" MODIFIED="1534336338575" TEXT="Werkzeug">
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1534334810537" ID="ID_1326202399" MODIFIED="1534340000558" TEXT="Link zum Widget">
<node COLOR="#338800" CREATED="1534334810537" FOLDED="true" ID="ID_1326202399" MODIFIED="1534436661859" TEXT="Link zum Widget">
<linktarget COLOR="#a66b86" DESTINATION="ID_1326202399" ENDARROW="Default" ENDINCLINATION="279;-1333;" ID="Arrow_ID_664593340" SOURCE="ID_203145360" STARTARROW="None" STARTINCLINATION="-1296;0;"/>
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1534334914667" ID="ID_511749457" MODIFIED="1534334932930" TEXT="smart-Ref">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1534334939159" ID="ID_1853703071" MODIFIED="1534335238329" TEXT="leer konstruierbar">
<icon BUILTIN="flag-yellow"/>
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1534334914667" ID="ID_511749457" MODIFIED="1534436573129" TEXT="smart-Ref">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1534334939159" ID="ID_1853703071" MODIFIED="1534436570387" TEXT="leer konstruierbar">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1534334944207" ID="ID_1989000221" MODIFIED="1534342998767" TEXT="bool testbar">
<icon BUILTIN="button_ok"/>
@ -3218,8 +3218,8 @@
</richcontent>
<icon BUILTIN="idea"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1534336127895" HGAP="21" ID="ID_352867763" MODIFIED="1534340006778" TEXT="WLink_test" VSHIFT="9">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1534336127895" HGAP="21" ID="ID_352867763" MODIFIED="1534436566889" TEXT="WLink_test" VSHIFT="9">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1534340031277" ID="ID_506504727" MODIFIED="1534341378412" TEXT="test-dummy-trackable">
<icon BUILTIN="button_ok"/>
</node>
@ -3233,6 +3233,15 @@
<icon BUILTIN="button_ok"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1534436582390" ID="ID_880451727" MODIFIED="1534436621371" TEXT="nur EX_SANE">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1534436589806" ID="ID_1530771493" MODIFIED="1534436645751" TEXT="unter der Annahme, da&#xdf; sigc::trackable EX_SANE ist">
<icon BUILTIN="info"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1534436609323" ID="ID_1966799512" MODIFIED="1534436641103" TEXT="nicht wirklich gepr&#xfc;ft">
<icon BUILTIN="yes"/>
</node>
</node>
</node>
</node>
</node>