mimic++ v4
Loading...
Searching...
No Matches
mimicpp::Watched< Base, Watchers > Class Template Reference

CRTP-type, inheriting first from all Watchers and then Base, thus effectively couple them all together. More...

#include <ObjectWatcher.hpp>

Inheritance diagram for mimicpp::Watched< Base, Watchers >:
[legend]
Collaboration diagram for mimicpp::Watched< Base, Watchers >:
[legend]

Public Member Functions

 ~Watched ()=default
 
 Watched (const Watched &)=default
 
Watchedoperator= (const Watched &)=default
 
 Watched (Watched &&)=default
 
Watchedoperator= (Watched &&)=default
 

Detailed Description

template<typename Base, object_watcher... Watchers>
requires std::same_as<Base, std::remove_cvref_t<Base>>
class mimicpp::Watched< Base, Watchers >

CRTP-type, inheriting first from all Watchers and then Base, thus effectively couple them all together.

Template Parameters
BaseThe main type.
WatchersAll utilized watcher types.

Move-constructor and -assignment-operator

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.

See also
https://en.cppreference.com/w/cpp/error/terminate Nevertheless, this is usually fine, as watchers are merely used under controlled circumstances and to guarantee the expected behavior. If a violation is reported, an appropriate output will be generated, which should be enough of a hint to track down the bug.

Destructor

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.

See also
https://en.cppreference.com/w/cpp/language/noexcept_spec
https://en.cppreference.com/w/cpp/error/terminate

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.

Constructor & Destructor Documentation

◆ ~Watched()

template<typename Base , object_watcher... Watchers>
mimicpp::Watched< Base, Watchers >::~Watched ( )
default

◆ Watched() [1/2]

template<typename Base , object_watcher... Watchers>
mimicpp::Watched< Base, Watchers >::Watched ( const Watched< Base, Watchers > & )
default

◆ Watched() [2/2]

template<typename Base , object_watcher... Watchers>
mimicpp::Watched< Base, Watchers >::Watched ( Watched< Base, Watchers > && )
default

Member Function Documentation

◆ operator=() [1/2]

template<typename Base , object_watcher... Watchers>
Watched & mimicpp::Watched< Base, Watchers >::operator= ( const Watched< Base, Watchers > & )
default

◆ operator=() [2/2]

template<typename Base , object_watcher... Watchers>
Watched & mimicpp::Watched< Base, Watchers >::operator= ( Watched< Base, Watchers > && )
default

The documentation for this class was generated from the following file: