|
mimic++ v9.2.1
|
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 |