mimic++ v4
|
CRTP-type, inheriting first from all Watchers
and then Base
, thus effectively couple them all together.
More...
#include <ObjectWatcher.hpp>
Public Member Functions | |
~Watched ()=default | |
Watched (const Watched &)=default | |
Watched & | operator= (const Watched &)=default |
Watched (Watched &&)=default | |
Watched & | operator= (Watched &&)=default |
CRTP-type, inheriting first from all Watchers
and then Base
, thus effectively couple them all together.
Base | The main type. |
Watchers | All utilized watcher types. |
Watched
automatically detects the specifications of the Base
move-constructor and -assignment-operator, regardless of the Watchers
specifications. This is done, so that the Watched
instance does mimic the interface of Base
as closely as possible.
This is important to note, as this has implications when a RelocationWatcher
is utilized. RelocationWatcher
may, during either move-construction or -assignment, report violations to the currently active reporter. This reporter has to act accordingly, by either throwing an exception or terminating the program.
So, if reporter throws due to a detected violation and the move-operation is declared noexcept
, this will inevitable lead to a std::terminate
.
Watched
automatically detects, whether Base
has a virtual destructor and applies override
if that's the case. It also forces the same noexcept
-ness for the destruct: If Base
is nothrow destructible, Watched
is it, too.
This is important to note, as this has implications when a LifetimeWatcher
is utilized. LifetimeWatcher
may, during destruction, report violations to the currently active reporter. This reporter has to act accordingly, by either throwing an exception or terminating the program.
As the destructor of the LifetimeWatcher
will effectively be called from ~Watched
, this will lead to a call to std::terminate
, if Base
has a noexcept
destructor (which is very likely, as it's a very strong default) and the reporter propagates the violation via an exception.
There is no real way around that, beside explicitly ~Watched
as noexcept(false)
. Unfortunately, this would lead to inconsistencies with noexcept
declared virtual
destructors, because this requires all subclasses to match that specification. Besides that, there is an even stronger argument to strictly follow what Base
offers: A Watched
object should be as close to the original Base
type as possible. If one wants to store a Watched<Base>
inside e.g. std::vector
and ~Watched
would have a different noexcept
specification than Base
, that would lead to behavior changes. This should never be the case, as mocks are expected to behave like an actual implementation-object.
So, what does all of this mean?
Actually, there are no implications to working tests. If they satisfy the expectations, no one will notice anything different. When it comes to a violation, which is detected by the destructor of a LifetimeWatcher
, the reporter will be notified and should print the no-match report to the console. After that, the program will than probably terminate (or halt, if a debugger is attached), but you should at least have an idea, which test is affected.
|
default |
|
default |
|
default |
|
default |
|
default |