Boost::signals2 passing invalid data - c++

I've been a C/C++ developer for about 20 years now, but templates have always been a weak spot for me. With template programming becoming ever more useful, and complicated, in the C++11 and C++14 standards, I decided to try an exercise to learn. I've been moderately successful, but I have an issue I'm having problems with. I have the following class:
namespace Events {
// Place your new EventManager events here
static const uint32_t StatsData = 0;
static const uint32_t StatsRequest = 1;
static const uint32_t StatsReply = 2;
static const uint32_t ApplianceStatsRequest = 3;
static const uint32_t ApplianceStatsReply = 4;
static const uint32_t NullEvent = 5;
};
class EventManager {
public:
static EventManager *instance() {
if (Instance)
return Instance;
return new EventManager();
};
static void destroy() {
delete Instance;
Instance = nullptr;
}
template<typename T>
bool consume_event(uint32_t event, std::function<T> func) {
if (_event_map.find(event) == _event_map.end())
// Create the signal, in true RAII style
_event_map[event] = new boost::signals2::signal<T>();
boost::any_cast<boost::signals2::signal<T> *>(_event_map[event])->connect(func);
return true;
}
void emit(uint32_t event) {
if (_event_map.find(event) == _event_map.end())
return;
try {
boost::signals2::signal<void()> *sig =
boost::any_cast<boost::signals2::signal<void()> *>(_event_map[event]);
(*sig)();
}
catch (boost::bad_any_cast &e) {
SYSLOG(ERROR) << "Caught instance of boost::bad_any_cast: " << e.what();
abort();
}
}
template<typename... Args>
void emit(uint32_t event, Args... args) {
if (_event_map.find(event) == _event_map.end())
return;
try {
boost::signals2::signal<void(Args...)> *sig =
boost::any_cast<boost::signals2::signal<void(Args...)> *>(_event_map[event]);
(*sig)(args...);
}
catch (boost::bad_any_cast &e) {
SYSLOG(ERROR) << "Caught instance of boost::bad_any_cast: " << e.what();
abort();
}
}
private:
EventManager() { Instance = this; };
~EventManager() { Instance = nullptr; };
static EventManager *Instance;
std::map<uint32_t, boost::any> _event_map;
};
This code would potentially go into a large framework that loads multiple modules which are dynamic libraries on linux. The idea would be for a given module to be able to call:
consume_event<ParamTypes><EventNumber, SomeCallack)
The callback may be a function with signature void(ParamTypes), or the result of std::bind() on a function with signature void(ParamTypes).
Another module would then be able to call:
emit<ParamTypes>(EventNumber, ParamValues)
and each module that had called consume_event, would have it's handler called with ParamValues.
This seems to work in almost every case, except when I pass a reference to a custom class, like this:
std::cout << "Sending stats data with ref: " << std::hex << ip_entry.second << std::endl;
emit<ip_stats_t &>(Events::StatsData, *ip_entry.second);
In this case, the function that is connected to the signal, receives 0xa, and promptly crashes when it tries to treat it as an ip_stats_t &.
The output is:
Sending stats data with ref: 0x7fbbc4177d50 <- This is the output of the line seen above
ips addr: 0xa << this is from the function that gets called by the signal.
Update: I just noticed it does the same thing when passing any variable by reference, not just the custom class above.
Additionally, please note that there is no SSCCE in this question because any SSCCE invariable works. The problem does not occur until the working code is put into the above framework.
Update2: The real question here is, how can this design be made better. This one not only doesn't work properly, but syntactically, it stinks. it's ugly, inelegant, and really, there's nothing good about it, except that it did what I wanted it to do and increased my understanding of templates.
Update3: I have now 100% confirmed that this has nothing to do with the data type that I'm passing. If I pass any variable by reference, the slot always receives 0xa as the address of the reference. This includes std::strings, and even ints. If I pass any variable by value, the copy constructor of that value eventually receives 0xa as the reference of the value to copy from. This only happens when calling a slot in module B from a signal created in module A. What am I missing?
Any ideas?
Thanks!

UPDATED I've since come up with a demonstration that would appear to be closer to what you were trying to achieve:
#lk75 For fun, here's an approach that abstracts the event mechanism in a fairly extensible way, while
not being overly complicated
not requiring calling signature to be repeated all over the place (it's in Traits now)
not leaking signals by using true RAII style (SCNR). No more use of new or delete!
See it Live On Coliru.
Note how I simplified the singleton and turned both consume_event and emit into one-liners now:
static EventManager& instance() {
static EventManager instance;
return instance;
};
template <EventId event, typename F>
bool consume_event(F&& func) {
get_slot<event>().connect(std::forward<F>(func));
return true;
}
template <EventId event, typename... Args>
void emit(Args&&... args) {
get_slot<event>()(std::forward<Args>(args)...);
}
Full Code
For reference:
Live On Coliru
#include <boost/any.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/signals2/signal.hpp>
#include <iostream>
#include <memory>
#include <string>
struct ip_stats_t {
std::string canary;
};
enum class EventId : uint32_t {
// Place your new EventManager events here
StatsData = 0,
StatsRequest = 1,
StatsReply = 2,
ApplianceStatsRequest = 3,
ApplianceStatsReply = 4,
NullEvent = 5, // Not implemented
};
namespace Events {
template <EventId> struct Traits;
template <> struct Traits<EventId::StatsData> { using signal_type = boost::signals2::signal<void(int)>; } ;
template <> struct Traits<EventId::StatsRequest> { using signal_type = boost::signals2::signal<void(bool, bool)>; } ;
template <> struct Traits<EventId::StatsReply> { using signal_type = boost::signals2::signal<void(std::string)>; } ;
template <> struct Traits<EventId::ApplianceStatsRequest> { using signal_type = boost::signals2::signal<void(double, ip_stats_t&)>; } ;
//template <> struct Traits<EventId::NullEvent> { using signal_type = boost::signals2::signal<void()>; } ;
template <> struct Traits<EventId::ApplianceStatsReply> : Traits<EventId::ApplianceStatsRequest> { };
}
class EventManager {
public:
static EventManager& instance() {
static EventManager instance;
return instance;
};
template <EventId event, typename F>
bool consume_event(F&& func) {
get_slot<event>().connect(std::forward<F>(func));
return true;
}
template <EventId event, typename... Args>
void emit(Args&&... args) {
get_slot<event>()(std::forward<Args>(args)...);
}
private:
template <EventId event, typename Slot = typename Events::Traits<event>::signal_type, typename SlotPtr = boost::shared_ptr<Slot> >
Slot& get_slot() {
try {
if (_event_map.find(event) == _event_map.end())
_event_map.emplace(event, boost::make_shared<Slot>());
return *boost::any_cast<SlotPtr>(_event_map[event]);
}
catch (boost::bad_any_cast const &e) {
std::cerr << "Caught instance of boost::bad_any_cast: " << e.what() << " on event #" << static_cast<uint32_t>(event) << "\n";
abort();
}
}
EventManager() = default;
std::map<EventId, boost::any> _event_map;
};
int main() {
auto& emgr = EventManager::instance();
emgr.consume_event<EventId::ApplianceStatsRequest>([](double d, ip_stats_t& v) {
std::cout << "d: " << d << ", v.canary: " << v.canary << "\n";
});
emgr.consume_event<EventId::ApplianceStatsRequest>([](double d, ip_stats_t& v) {
std::cout << "And you can register more than one\n";
});
ip_stats_t v { "This is statically checked" };
emgr.emit<EventId::ApplianceStatsRequest>(3.142f, v);
emgr.emit<EventId::StatsData>(42); // no connection, but works
emgr.consume_event<EventId::StatsData>([](int) { std::cout << "Now it's connected\n"; });
emgr.emit<EventId::StatsData>(42); // now with connection!
#if 0
emgr.emit<EventId::ApplianceStatsRequest>(); // error: no match for call to ‘(boost::signals2::signal<void(double, ip_stats_t&)>) ()’
emgr.consume_event<EventId::NullEvent>([]{}); // use of incomplete type Traits<NullEvent>
#endif
}
Old answer:
You seem to have trouble with the variadic forwarding:
(*sig)(std::forward<Args>(args)...);
Also, forwarding really makes sense only when taking the arguments by "universal reference":
template<typename... Args>
void emit(uint32_t event, Args&&... args) { // NOTE!!
However, you do not rely on argument type deduction to get the actual value categories (rvalue vs. lvalue). And, rightly so (because the compiler would likely never get the exact argument types "right" to match the stored signal (making the any_cast fail at best, or invoke Undefined Behaviour at best.)
So in this case, you should dispense with the whole forwarding business:
template<typename... Args> using Sig = boost::signals2::signal<void(Args...)>;
template<typename... Args>
void emit(uint32_t event, Args... args) {
if (_event_map.find(event) == _event_map.end())
return;
try {
Sig<Args...> *sig = boost::any_cast<Sig<Args...> *>(_event_map[event]);
(*sig)(args...);
}
catch (boost::bad_any_cast &e) {
std::cerr << "Caught instance of boost::bad_any_cast: " << e.what();
abort();
}
}
Full demo program: Live On Coliru
#include <boost/any.hpp>
#include <boost/signals2/signal.hpp>
#include <iostream>
#include <string>
struct ip_stats_t {
std::string canary;
};
template<typename... Args> using Sig = boost::signals2::signal<void(Args...)>;
std::map<uint32_t, boost::any> _event_map;
template<typename... Args>
void emit(uint32_t event, Args&&... args) {
if (_event_map.find(event) == _event_map.end())
return;
try {
Sig<Args...> *sig = boost::any_cast<Sig<Args...> *>(_event_map[event]);
(*sig)(std::forward<Args>(args)...);
}
catch (boost::bad_any_cast &e) {
std::cerr << "Caught instance of boost::bad_any_cast: " << e.what();
abort();
}
}
int main()
{
Sig<int, double> sig1;
Sig<ip_stats_t&> sig2;
sig1.connect([](int i, double d) { std::cout << "Sig1 handler: i = " << i << ", d = " << d << "\n"; });
sig2.connect([](ip_stats_t& v) { std::cout << "Sig2 handler: canary = " << v.canary << "\n"; });
_event_map[1] = &sig1;
_event_map[2] = &sig2;
emit<int, double>(1, 42, 3.14);
ip_stats_t instance { "Hello world" }, *ptr = &instance;
emit<ip_stats_t&>(2, *ptr);
}

The following code, which is Sehe's revised code without the boost::signals solved my problem completely. It would appear that boost::signals was having issues passing any data whatsoever across module boundries. Replacing it with a simple vector of functions works in all cases, and is faster anyway!
enum class EventId : uint32_t {
// Place your new EventManager events here
StatsData = 0,
StatsRequest = 1,
StatsReply = 2,
ApplianceStatsRequest = 3,
ApplianceStatsReply = 4,
};
struct ip_stats_t;
namespace Events {
template <EventId> struct Traits;
template <> struct Traits<EventId::StatsData>
{ using signal_vec = std::vector<std::function<void(ip_stats_t &)>>; } ;
template <> struct Traits<EventId::StatsRequest>
{ using signal_vec = std::vector<std::function<void(std::ostream &)>>; } ;
template <> struct Traits<EventId::StatsReply>
{ using signal_vec = std::vector<std::function<void(std::string &)>>; } ;
template <> struct Traits<EventId::ApplianceStatsRequest> :
Traits<EventId::StatsRequest> {};
template <> struct Traits<EventId::ApplianceStatsReply> :
Traits<EventId::StatsReply> {};
}
class EventManager {
public:
static EventManager& instance() {
static EventManager instance;
return instance;
};
template <EventId event, typename F>
void consume_event(F&& func) {
get_slot<event>().push_back(std::forward<F>(func));
}
template <EventId event, typename... Args>
void emit(Args&&... args) {
for (auto &vi : get_slot<event>()) {
vi(std::forward<Args>(args)...);
}
}
private:
template <EventId event,
typename Slot = typename Events::Traits<event>::signal_vec,
typename SlotPtr = std::shared_ptr<Slot>>
Slot& get_slot() {
if (_event_map.find(event) == _event_map.end())
_event_map.emplace(event, std::make_shared<Slot>());
try {
return *boost::any_cast<SlotPtr>(_event_map[event]);
}
catch (boost::bad_any_cast const &e) {
std::cerr << e.what() << " on event #" << static_cast<uint32_t>(event) << "\n";
abort();
}
}
EventManager() = default;
std::map<EventId, boost::any> _event_map;
};

Related

C++ Using container as a template type

I am trying to supply the underlying container/data structure for a class via template argument. I'd like to have the underlying container always contain a certain pointer type and I want the maximum amount of variable allowed to be stored to be limited, no matter which container is used.
I managed to make something simple work for std::vector and std::array .
However, for the std::array example there are some weird things I have to do so it compiles.
Here is the test code:
#include <iostream>
#include <type_traits>
#include <vector>
#include <array>
#include <algorithm>
#include <map>
class CustomVariableIF {
public:
void getFoo() const {
std::cout << "Moh!" << std::endl;
return;
}
};
template <typename T>
class CustomVariable: public CustomVariableIF {
public:
CustomVariable(T initValue): value(initValue) {}
void setValue(T newValue) {
value = newValue;
}
T getValue() const {
return value;
}
private:
T value = 0;
};
template <typename TContainer>
class CustomVariableSetBase {
public:
using value_type = typename TContainer::value_type;
/* This allows std::array, std::vector and std::map as underlying container */
static_assert(std::is_same<value_type, CustomVariableIF*>::value or
std::is_same<value_type, std::pair<const uint32_t, CustomVariableIF*>>::value,
"Invalid template type!");
CustomVariableSetBase(const size_t maxFillCount): maxFillCount(maxFillCount) {}
virtual~ CustomVariableSetBase() {};
template<class Q = TContainer>
typename std::enable_if<std::is_same<typename Q::value_type, CustomVariableIF*>::value>::type
callFoos() {
size_t currentIdx = 0;
for (auto& var: container) {
if(var == nullptr) {
return;
}
if(currentIdx < fillCount) {
var->getFoo();
currentIdx++;
}
}
}
/* Used if underlying container is a std::map */
template<class Q = TContainer> typename
std::enable_if<std::is_same<typename Q::value_type,
std::pair<const uint32_t, CustomVariableIF*>>::value>::type
callFoos() {
for (auto& var: container) {
var.second->getFoo();
}
}
protected:
virtual void registerVariable(CustomVariableIF* variable) = 0;
TContainer container;
size_t fillCount = 0;
const size_t maxFillCount;
};
class CustomVariableMap: public CustomVariableSetBase<std::map<uint32_t, CustomVariableIF*>> {
public:
CustomVariableMap(const size_t maxVars): CustomVariableSetBase(maxVars) {};
virtual~ CustomVariableMap() {};
void registerVariable(CustomVariableIF* variable) override {
if(fillCount < maxFillCount) {
container.emplace(idCounter, variable);
lastId = idCounter;
idCounter++;
fillCount++;
}
else {
std::cerr << "CustomVariableMap::registerVariable: Map container is full!" << std::endl;
}
return;
}
uint32_t getIdOfLastAddedVariable() const {
return lastId;
}
private:
uint32_t idCounter = 0;
uint32_t lastId = 0;
};
class CustomVariableSet: public CustomVariableSetBase<std::vector<CustomVariableIF*>> {
public:
CustomVariableSet(const size_t maxVars): CustomVariableSetBase(maxVars) {};
virtual~ CustomVariableSet() {};
void registerVariable(CustomVariableIF* variable) override {
if(fillCount < maxFillCount) {
container.push_back(variable);
fillCount++;
}
else {
std::cerr << "CustomVariableSet::registerVariable: Vector container is full!" <<
std::endl;
}
return;
}
private:
};
template <uint8_t NUM_VARIABLES>
class CustomVariableStaticSet:
public CustomVariableSetBase<std::array<CustomVariableIF*, NUM_VARIABLES>> {
public:
using ArrayBase = std::array<CustomVariableIF*, NUM_VARIABLES>;
CustomVariableStaticSet(): CustomVariableSetBase<ArrayBase>(NUM_VARIABLES) {};
virtual void registerVariable(CustomVariableIF* variable) {
if(this->fillCount < NUM_VARIABLES) {
this->container[this->fillCount] = variable;
this->fillCount++;
}
else {
std::cerr << "CustomVariableStaticSet::registerVariable: Array container is full!" <<
std::endl;
}
}
private:
};
int main() {
using namespace std;
CustomVariableSet testSet(2);
CustomVariableStaticSet<5> testSet2;
CustomVariableMap testSet3(2);
CustomVariable<int> someVar1(5);
testSet.registerVariable(&someVar1);
testSet.registerVariable(&someVar1);
std::cout << "Moh! should be printed twice!" << std::endl;
testSet.callFoos();
testSet2.registerVariable(&someVar1);
testSet2.registerVariable(&someVar1);
std::cout << "Moh! should be printed twice!" << std::endl;
testSet2.callFoos();
testSet3.registerVariable(&someVar1);
testSet3.registerVariable(&someVar1);
std::cout << "Moh! should be printed twice!" << std::endl;
testSet3.callFoos();
std::cout << "Should yield two errors!" << std::endl;
testSet.registerVariable(&someVar1);
testSet2.registerVariable(&someVar1);
testSet3.registerVariable(&someVar1);
}
The class which has std::vector as the underlying container works like I expected.
For the std::array one, I have to write the whole template typename again in the constructor initializer list. I also have to write this-> everytime I try to access a member of the base class.
Can anyone explain to me why I have to do these steps for the class using std::array? Thanks a lot in advance!
Kind Regards
RM
Alright, after some more research I found the more in-depth answer here:
https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-members
(Question: Why am I getting errors when my template-derived-class uses a member it inherits from its template-base-class?)
A very useful read in general when working with templates. I also updated my test code to include an implementation for std::map as the underlying container, which was tricky because the value type is actually different than for the other two containers. There are still some nullptr checks and some other things missing to be really clean, but it worked for me and maybe it can help some other people.

How emulate a templatised std::function in C++

Following is a basic instance of what I am doing in my C++ program. I have a list of listeners which are all std::functions. I have a concept DataType which means what kind of data the listener is interested in. The idea here is the same as publish-subscribe pattern. A method interested in certain kind of data should be able to add itself to the list of listeners using AddListener. Some methods are added & they receive a callback whenever required.
The program works fine !!
#include <iostream>
#include <functional>
#include <vector>
#include <string>
enum class DataType {
Type_1,
Type_2
// and so on
};
typedef std::function<void(std::pair<DataType, std::string>)> MyListenerType;
//template <typename T>
//typedef std::function<void(T>)> MyListenerType;
// How can I emulate the above so that a method passing any kind of primitive data-type namely "int, bool, float or double" can be added into
// my vector of listners.
std::vector<MyListenerType> my_data_listeners_1;
std::vector<MyListenerType> my_data_listeners_2;
void ListenerMethod_Instance_1(std::pair<DataType, std::string> information) {
DataType data_type = information.first;
std::string message = information.second;
std::cout << "ListenerMethod_Instance_1 called with message " << message << "\n";
}
void ListenerMethod_Instance_2(std::pair<DataType, std::string> information) {
DataType data_type = information.first;
std::string message = information.second;
std::cout << "ListenerMethod_Instance_2 called with message " << message << "\n";
}
void AddListener (MyListenerType listener, DataType type_of_interest) {
if (DataType::Type_1 == type_of_interest) {
my_data_listeners_1.push_back(listener);
std::cout << "Added a method instance for DataType::Type_1" << "\n";
}
else if (DataType::Type_2 == type_of_interest) {
my_data_listeners_2.push_back(listener);
std::cout << "Added a method instance for DataType::Type_2" << "\n";
}
else {
std::cout << "Listener type not supported" << "\n";
}
}
void CallAllListnersWhohaveSuscribed() {
if (!my_data_listeners_1.empty()) {
std::string send_message_1 = "some message 123";
std::pair <DataType, std::string> info_to_send_1 = std::make_pair (DataType::Type_1, send_message_1);
for(auto const &listener : my_data_listeners_1) {
listener(info_to_send_1);
}
}
if (!my_data_listeners_2.empty()) {
std::string send_message_2 = "some message 456";
std::pair <DataType, std::string> info_to_send_2 = std::make_pair (DataType::Type_2, send_message_2);
for(auto const &listener : my_data_listeners_2) {
listener(info_to_send_2);
}
}
}
int main() {
// Add ListenerMethod_Instance_1 for instance
DataType data_type_1 = DataType::Type_1;
auto listener_instance_1 = std::bind(ListenerMethod_Instance_1, std::placeholders::_1);
AddListener(listener_instance_1, data_type_1);
// Add ListenerMethod_Instance_2 for instance
DataType data_type_2 = DataType::Type_2;
auto listener_instance_2 = std::bind(ListenerMethod_Instance_2, std::placeholders::_1);
AddListener(listener_instance_2, data_type_2);
CallAllListnersWhohaveSuscribed();
return 0;
}
Following is the output of the program:
./stdFunctionTest
Added a method instance for DataType::Type_1
Added a method instance for DataType::Type_2
ListenerMethod_Instance_1 called with message some message 123
ListenerMethod_Instance_2 called with message some message 456
But here is how I want to modify & struggling with. The caveat is that every ListenerMethod_Instance_1 & ListenerMethod_Instance_2 have to parse the pair to get their info which I don't want to. I want to enable a method of any C++ primitive data type be it "int, bool, float or double" to be able to be added into the listeners vector & receive the callback. For example following method should be "add-able" into AddListener.
void ListenerMethod_Instance_3(int integer_data) {
std::cout << "ListenerMethod_Instance_3 called with integer_data " << integer_data << "\n";
}
Looking at this link here looks somewhat possible someway. But I'm struggling to adapt it to my use-case here. Please suggest.
So, basically how can I achieve templates functionality with std::functions ?
struct anything_view_t {
void* ptr=0;
template<class T, std::enable_if_t<!std::is_same<anything_view_t, std::decay_t<T>>{}, int> =0>
anything_view_t(T&&t):ptr(std::addressof(t)){}
anything_view_t()=default;
anything_view_t(anything_view_t const&)=default;
anything_view_t& operator=(anything_view_t const&)=default;
template<class T>
operator T() const { return *static_cast<T*>(ptr); }
};
this is a very unsafe type erasing view of anything.
struct any_callbacks {
std::unordered_map<std::type_index, std::vector<std::function<void(anything_view_t)>>> table;
template<class T>
void add_callback( std::function<void(T)> f ){
table[typeid(T)].push_back(f);
}
template<class T>
void invoke_callbacks(T t) const {
auto it = table.find(typeid(T));
if (it==table.end()) return;
for(auto&&f:it->second)
f(t);
}
};
something like the above should work. The type T must match exactly. References not supported. Code not compiled, design is sound, probably has typos.
This is not restructed to primitive types. You should pass T explicitly, don't rely on deduction as that is fragile.

Boost ASIO threadpool with a thread timeout

I'm using the Boost ASIO library as a threadpool, which is widely described. However, I want to interrupt each thread, should the thread process for longer than 1 second and move onto the next posted task for the thread.
I can easily implement this using a separate deadline_timer, which is reset if the thread finishes before the deadline or interrupts the thread should the task go on for too long. However I assumed this would be built into ASIO. As it seems natural to have a task, with a timeout for network operations. But I can't see anything in the API for it, to do that succinctly.
Can anyone tell me if this functionality already exists? Or should I implement it the way I described?
Here's a quick solution I knocked together.
It requires that your submitted function objects accept an argument of type exec_context.
The task running in the io_service can query the .canceled() accessor (which is atomic) to determine whether it should cancel early.
It can then either throw an exception or return whatever value it intended to return.
The caller submits via the submit function. This function wraps the worker function with the context object and marshals its return value and/or exception into a std::future.
The caller can then query or wait on this future (or ignore it) as appropriate.
The caller gets a handle object, which has the method cancel() on it. Using this handle, the caller can either cancel, query or wait on the submitted task.
Hope it helps. It was fun to write.
#include <boost/asio.hpp>
#include <iostream>
#include <atomic>
#include <thread>
#include <chrono>
#include <future>
#include <stdexcept>
#include <exception>
#include <utility>
#include <type_traits>
//
// an object to allow the caller to communicate a cancellation request to the
// submitted task
//
struct exec_controller
{
/// #returns previous cancellation request state;
bool notify_cancel()
{
return _should_cancel.exchange(true);
}
bool should_cancel() const {
return _should_cancel;
}
private:
std::atomic<bool> _should_cancel = { false };
};
template<class Ret>
struct exec_state : std::enable_shared_from_this<exec_state<Ret>>
{
using return_type = Ret;
bool notify_cancel() {
return _controller.notify_cancel();
}
std::shared_ptr<exec_controller>
get_controller_ptr() {
return std::shared_ptr<exec_controller>(this->shared_from_this(),
std::addressof(_controller));
}
std::promise<return_type>& promise() { return _promise; }
private:
std::promise<return_type> _promise;
exec_controller _controller;
};
struct applyer;
struct exec_context
{
exec_context(std::shared_ptr<exec_controller> impl)
: _impl(impl)
{}
bool canceled() const {
return _impl->should_cancel();
}
private:
friend applyer;
std::shared_ptr<exec_controller> _impl;
};
struct applyer
{
template<class F, class Ret>
void operator()(F& f, std::shared_ptr<exec_state<Ret>> const& p) const
{
try {
p->promise().set_value(f(exec_context { p->get_controller_ptr() }));
}
catch(...) {
p->promise().set_exception(std::current_exception());
}
}
template<class F>
void operator()(F& f, std::shared_ptr<exec_state<void>> const& p) const
{
try {
f(exec_context { p->get_controller_ptr() });
p->promise().set_value();
}
catch(...) {
p->promise().set_exception(std::current_exception());
}
}
};
template<class Ret>
struct exec_result
{
using return_type = Ret;
exec_result(std::shared_ptr<exec_state<return_type>> p)
: _impl(p)
{}
bool cancel() {
return _impl->notify_cancel();
}
std::future<Ret>& get_future()
{
return _future;
}
private:
std::shared_ptr<exec_state<return_type>> _impl;
std::future<return_type> _future { _impl->promise().get_future() };
};
template<class Executor, class F>
auto submit(Executor& exec, F&& f)
{
using function_type = std::decay_t<F>;
using result_type = std::result_of_t<function_type(exec_context)>;
using state_type = exec_state<result_type>;
auto shared_state = std::make_shared<state_type>();
exec.post([shared_state, f = std::forward<F>(f)]
{
applyer()(f, shared_state);
});
return exec_result<result_type>(std::move(shared_state));
}
int main()
{
using namespace std::literals;
boost::asio::io_service ios;
boost::asio::io_service::strand strand(ios);
boost::asio::io_service::work work(ios);
std::thread runner([&] { ios.run(); });
std::thread runner2([&] { ios.run(); });
auto func = [](auto context)
{
for(int i = 0 ; i < 1000 ; ++i)
{
if (context.canceled())
throw std::runtime_error("canceled");
std::this_thread::sleep_for(100ms);
}
};
auto handle = submit(strand, func);
auto handle2 = submit(ios, [](auto context) { return 2 + 2; });
// cancel the handle, or wait on it as you wish
std::this_thread::sleep_for(1s);
handle.cancel();
handle2.cancel(); // prove that late cancellation is a nop
try {
std::cout << "2 + 2 is " << handle2.get_future().get() << std::endl;
}
catch(std::exception& e)
{
std::cerr << "failed to add 2 + 2 : " << e.what() << std::endl;
}
try {
handle.get_future().get();
std::cout << "task completed" << std::endl;
}
catch(std::exception const& e) {
std::cout << "task threw exception: " << e.what() << std::endl;
}
ios.stop();
runner.join();
runner2.join();
}
update: v2 adds some privacy protection to the classes, demonstrates 2 simultaneous tasks.
expected output:
2 + 2 is 4
task threw exception: canceled

Best practices + syntax for implementing a "contextmanager" in C++

Our Python codebase has metrics-related code that looks like this:
class Timer:
def __enter__(self, name):
self.name = name
self.start = time.time()
def __exit__(self):
elapsed = time.time() - self.start
log.info('%s took %f seconds' % (self.name, elapsed))
...
with Timer('foo'):
do some work
with Timer('bar') as named_timer:
do some work
named_timer.some_mutative_method()
do some more work
In Python's terminology, the timer is a contextmanager.
Now we want to implement the same thing in C++, with an equally nice syntax. Unfortunately, C++ doesn't have with. So the "obvious" idiom would be (classical RAII)
class Timer {
Timer(std::string name) : name_(std::move(name)) {}
~Timer() { /* ... */ }
};
if (true) {
Timer t("foo");
do some work
}
if (true) {
Timer named_timer("bar");
do some work
named_timer.some_mutative_method();
do some more work
}
But this is extremely ugly syntactic salt: it's many lines longer than it needs to be, we had to introduce a name t for our "unnamed" timer (and the code breaks silently if we forget that name)... it's just ugly.
What are some syntactic idioms that people have used to deal with "contextmanagers" in C++?
I've thought of this abusive idea, which reduces the line-count but doesn't get rid of the name t:
// give Timer an implicit always-true conversion to bool
if (auto t = Timer("foo")) {
do some work
}
Or this architectural monstrosity, which I don't even trust myself to use correctly:
Timer("foo", [&](auto&) {
do some work
});
Timer("bar", [&](auto& named_timer) {
do some work
named_timer.some_mutative_method();
do some more work
});
where the constructor of Timer actually invokes the given lambda (with argument *this) and does the logging all in one go.
Neither of those ideas seems like a "best practice", though. Help me out here!
Another way to phrase the question might be: If you were designing std::lock_guard from scratch, how would you do it so as to eliminate as much boilerplate as possible? lock_guard is a perfect example of a contextmanager: it's a utility, it's intrinsically RAII, and you hardly ever want to bother naming it.
It's possible to mimic the Python syntax and semantics quite closely. The following test case compiles and has largely similar semantics to what you'd have in Python:
// https://github.com/KubaO/stackoverflown/tree/master/questions/pythonic-with-33088614
#include <cassert>
#include <cstdio>
#include <exception>
#include <iostream>
#include <optional>
#include <string>
#include <type_traits>
[...]
int main() {
// with Resource("foo"):
// print("* Doing work!\n")
with<Resource>("foo") >= [&] {
std::cout << "1. Doing work\n";
};
// with Resource("foo", True) as r:
// r.say("* Doing work too")
with<Resource>("bar", true) >= [&](auto &r) {
r.say("2. Doing work too");
};
for (bool succeed : {true, false}) {
// Shorthand for:
// try:
// with Resource("bar", succeed) as r:
// r.say("Hello")
// print("* Doing work\n")
// except:
// print("* Can't do work\n")
with<Resource>("bar", succeed) >= [&](auto &r) {
r.say("Hello");
std::cout << "3. Doing work\n";
} >= else_ >= [&] {
std::cout << "4. Can't do work\n";
};
}
}
That's given
class Resource {
const std::string str;
public:
const bool successful;
Resource(const Resource &) = delete;
Resource(Resource &&) = delete;
Resource(const std::string &str, bool succeed = true)
: str(str), successful(succeed) {}
void say(const std::string &s) {
std::cout << "Resource(" << str << ") says: " << s << "\n";
}
};
The with free function passes all the work to the with_impl class:
template <typename T, typename... Ts>
with_impl<T> with(Ts &&... args) {
return with_impl<T>(std::forward<Ts>(args)...);
}
How do we get there? First, we need a context_manager class: the traits class that implements the enter and exit methods - the equivalents of Python's __enter__ and __exit__. As soon as the is_detected type trait gets rolled into C++, this class can also easily forward to the compatible enter and exit methods of the class type T, thus mimicking Python's semantics even better. As it stands, the context manager is rather simple:
template <typename T>
class context_manager_base {
protected:
std::optional<T> context;
public:
T &get() { return context.value(); }
template <typename... Ts>
std::enable_if_t<std::is_constructible_v<T, Ts...>, bool> enter(Ts &&... args) {
context.emplace(std::forward<Ts>(args)...);
return true;
}
bool exit(std::exception_ptr) {
context.reset();
return true;
}
};
template <typename T>
class context_manager : public context_manager_base<T> {};
Let's see how this class would be specialized to wrap the Resource objects, or std::FILE *.
template <>
class context_manager<Resource> : public context_manager_base<Resource> {
public:
template <typename... Ts>
bool enter(Ts &&... args) {
context.emplace(std::forward<Ts>(args)...);
return context.value().successful;
}
};
template <>
class context_manager<std::FILE *> {
std::FILE *file;
public:
std::FILE *get() { return file; }
bool enter(const char *filename, const char *mode) {
file = std::fopen(filename, mode);
return file;
}
bool leave(std::exception_ptr) { return !file || (fclose(file) == 0); }
~context_manager() { leave({}); }
};
The implementation of the core functionality is in the with_impl type. Note how the exception handling within the suite (the first lambda) and the exit function mimic Python behavior.
static class else_t *else_;
class pass_exceptions_t {};
template <typename T>
class with_impl {
context_manager<T> mgr;
bool ok;
enum class Stage { WITH, ELSE, DONE } stage = Stage::WITH;
std::exception_ptr exception = {};
public:
with_impl(const with_impl &) = delete;
with_impl(with_impl &&) = delete;
template <typename... Ts>
explicit with_impl(Ts &&... args) {
try {
ok = mgr.enter(std::forward<Ts>(args)...);
} catch (...) {
ok = false;
}
}
template <typename... Ts>
explicit with_impl(pass_exceptions_t, Ts &&... args) {
ok = mgr.enter(std::forward<Ts>(args)...);
}
~with_impl() {
if (!mgr.exit(exception) && exception) std::rethrow_exception(exception);
}
with_impl &operator>=(else_t *) {
assert(stage == Stage::ELSE);
return *this;
}
template <typename Fn>
std::enable_if_t<std::is_invocable_r_v<void, Fn, decltype(mgr.get())>, with_impl &>
operator>=(Fn &&fn) {
assert(stage == Stage::WITH);
if (ok) try {
std::forward<Fn>(fn)(mgr.get());
} catch (...) {
exception = std::current_exception();
}
stage = Stage::ELSE;
return *this;
}
template <typename Fn>
std::enable_if_t<std::is_invocable_r_v<bool, Fn, decltype(mgr.get())>, with_impl &>
operator>=(Fn &&fn) {
assert(stage == Stage::WITH);
if (ok) try {
ok = std::forward<Fn>(fn)(mgr.get());
} catch (...) {
exception = std::current_exception();
}
stage = Stage::ELSE;
return *this;
}
template <typename Fn>
std::enable_if_t<std::is_invocable_r_v<void, Fn>, with_impl &> operator>=(Fn &&fn) {
assert(stage != Stage::DONE);
if (stage == Stage::WITH) {
if (ok) try {
std::forward<Fn>(fn)();
} catch (...) {
exception = std::current_exception();
}
stage = Stage::ELSE;
} else {
assert(stage == Stage::ELSE);
if (!ok) std::forward<Fn>(fn)();
if (!mgr.exit(exception) && exception) std::rethrow_exception(exception);
stage = Stage::DONE;
}
return *this;
}
template <typename Fn>
std::enable_if_t<std::is_invocable_r_v<bool, Fn>, with_impl &> operator>=(Fn &&fn) {
assert(stage != Stage::DONE);
if (stage == Stage::WITH) {
if (ok) try {
ok = std::forward<Fn>(fn)();
} catch (...) {
exception = std::current_exception();
}
stage = Stage::ELSE;
} else {
assert(stage == Stage::ELSE);
if (!ok) std::forward<Fn>(fn)();
if (!mgr.exit(exception) && exception) std::rethrow_exception(exception);
stage = Stage::DONE;
}
return *this;
}
};
Edit: After reading Dai's comment more carefully, and thinking a bit more, I realized this is a poor choice for C++ RAII. Why? Because you are logging in the destructor, this means you are doing io, and io can throw. C++ destructors should not emit exceptions. With python, writing a throwing __exit__ isn't necessarily awesome either, it can cause you to drop your first exception on the floor. But in python, you definitively know whether the code in the context manager has caused an exception or not. If it caused an exception, you can just omit your logging in __exit__ and pass through the exception. I leave my original answer below in case you have a context manager which doesn't risk throwing on exit.
The C++ version is 2 lines longer than the python version, one for each curly brace. If C++ is only two lines longer than python, that's doing well. Context managers are designed for this specific thing, RAII is more general and provides a strict superset of the functionality. If you want to know best practice, you already found it: have an anonymous scope and create the object at the beginning. This is idiomatic. You may find it ugly coming from python, but in the C++ world it's just fine. The same way someone from C++ will find context managers ugly in certain situations. FWIW I use both languages professionally and this doesn't bother me at all.
That said, I'll provide a cleaner approach for anonymous context managers. Your approach with constructing Timer with a lambda and immediately letting it destruct is pretty weird, so you're right to be suspicious. A better approach:
template <class F>
void with_timer(const std::string & name, F && f) {
Timer timer(name);
f();
}
Usage:
with_timer("hello", [&] {
do something;
});
This is equivalent to the anonymous context manager, in the sense that none of Timer's methods can be called other than construction and destruction. Also, it uses the "normal" class, so you can use the class when you need a named context manager, and this function otherwise. You could obviously write with_lock_guard in a very similar way. There it's even better as lock_guard doesn't have any member functions you're missing out on.
All that said, would I use with_lock_guard, or approve code written by a teammate that added in such a utility? No. One or two extra lines of code just doesn't matter; this function doesn't add enough utility to justify it's own existence. YMMV.
You don't need if( true ), C++ has "anonymous scopes" which can be used to restrict a scope's lifetime in much the same was as Python's with or C#s using (well, C# also has anonymous scopes too).
Like so:
doSomething();
{
Time timer("foo");
doSomethingElse();
}
doMoreStuff();
Just use bare curly-brackets.
However, I disagree with your idea of using RAII-semantics to instrument code like this as the timer destructor is non-trivial and has side-effects by-design. It might be ugly and repetitive, but I feel explicitly calling named startTimer, stopTimer and printTimer methods make the program more "correct" and self-documenting. Side-effects are bad, m'key?
I have recently started a C++ project to mimic Python's context manager as I migrate a python code base over to C++, found at https://github.com/batconjurer/contextual.
For this flow, you need to define a resource manager derived from an interface named IResource. Below, this is called Timer. It is in this class that the enter and exit functions are implemented. The context is just the code block that requires the resources, so it is passed via anonymous function.
The resource manager expects you to implement the IData struct which is uses to store the acquired resources. It actually only keeps a pointer to an IData instance.
For your use case, the following is an example implementation that compiles with C++17.
#include "include/contextual.h"
#include <ctime>
#include <chrono>
#include <thread>
using namespace Contextual;
namespace Contextual {
struct IData {
std::string name;
std::time_t start_time = std::time(NULL);
void reset_time() {
std::cout << "Time before restart: " << start_time << "\n";
std::time(&start_time);
std::cout << "Time after restart: " << start_time << "\n";
};
};
class Timer : public IResource<IData> {
private:
IData _data;
void enter() override {
std::time(&resources->start_time);
}
void exit(std::optional<std::exception> e) override {
double elapsed_time = std::time(NULL) - resources->start_time;
std::cout << resources->name << " took " << elapsed_time << " seconds.\n";
if (e) {
throw e.value();
}
}
public:
Timer(std::string &&name) : IResource<IData>(_data), _data(IData{name}){};
};
};
int main(){
With {
Timer(std::string("Foo")) + Context{
[&](IData* time_data) {
std::chrono::milliseconds sleeptime(5000);
std::this_thread::sleep_for(sleeptime); // In place of "Do some work"
time_data->reset_time(); // In place of "some_mutative_function()"
std::this_thread::sleep_for(sleeptime); // In place of "Do some work"
}
}
};
}
There are some nuisances I'm still working on (for example the fact that the IData struct had to be stored as a instance variable of the Timer since only a pointer to it is kept by IResource). And of course C++ exceptions aren't the nicest things.
Inspired by Dai's answer, I ended up with this code:
#include <iostream>
#include <chrono>
class Timer
{
std::chrono::high_resolution_clock::time_point startTime;
public:
Timer(): startTime(std::chrono::high_resolution_clock::now()){};
void elapsed()
{
auto endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsedTime = endTime - startTime;
std::cout << elapsedTime.count() << std::endl;
}
};
int main()
{
{
Timer timer=Timer();
std::cout << "This is some work" << std::endl;
timer.elapsed();
}
return 0;
}
I'm much more fluent in python than in c++; I'm not sure if it's idiomatic but it works for me.

How to write a wrapper over functions and member functions that executes some code before and after the wrapped function?

I'm trying to write some wrapper class or function that allows me to execute some code before and after the wrapped function.
float foo(int x, float y)
{
return x * y;
}
BOOST_PYTHON_MODULE(test)
{
boost::python::def("foo", <somehow wrap "&foo">);
}
Ideally, the wrapper should be generic, working for functions and member functions alike, with any signature.
More info:
I'm looking for a simple way to release/re-acquire the GIL around my expensive C++ calls without having to manually write thin wrappers like this:
float foo_wrapper(int x, float y)
{
Py_BEGIN_ALLOW_THREADS
int result = foo(x, y);
Py_END_ALLOW_THREADS
return result;
}
BOOST_PYTHON_MODULE(test)
{
boost::python::def("foo", &foo_wrapper);
}
This kind of wrapper will be repeated several times for all kinds of functions, and I would like to find a solution that would allow me to avoid coding all of them.
I have tried some approaches, but the best I could come with required the user to explicitly state the types of return values and parameters, like:
boost::python::def("foo", &wrap_gil<float, int, float>(&foo_wrapper));
But it seems to me it should be possible to just pass the pointer to the function (&foo_wrapper) and let the compiler figure out the types.
Does anyone know a technique I could use or point me in the right direction?
Cheers!
In this case, you can write a Functor class that wraps over your function, and then overload boost::python::detail::get_signature to accept your Functor!
UPDATE: Added support for member functions too!
Example:
#include <boost/shared_ptr.hpp>
#include <boost/python.hpp>
#include <boost/python/signature.hpp>
#include <boost/mpl/vector.hpp>
#include <iostream>
#include <string>
#include <sstream>
static boost::shared_ptr<std::ostringstream> test_stream_data;
std::ostringstream& test_stream()
{
if (!test_stream_data) {
test_stream_data.reset(new std::ostringstream);
}
return *test_stream_data;
}
std::string get_value_and_clear_test_stream()
{
std::string result;
if (test_stream_data) {
result = test_stream_data->str();
}
test_stream_data.reset(new std::ostringstream);
return result;
}
std::string func(int a, double b)
{
std::ostringstream oss;
oss << "func(a=" << a << ", b=" << b << ")";
std::string result = oss.str();
test_stream() << "- In " << result << std::endl;
return result;
}
class MyClass
{
public:
MyClass(std::string p_name)
: m_name(p_name)
{
test_stream() << "- In MyClass::MyClass(p_name=\"" << p_name << "\")" << std::endl;
}
MyClass(MyClass const& p_another)
: m_name(p_another.m_name)
{
test_stream()
<< "- In MyClass::MyClass(p_another=MyClass(\""
<< p_another.m_name << "\"))" << std::endl;
}
~MyClass()
{
test_stream() << "- In MyClass(\"" << this->m_name << "\")::~MyClass()" << std::endl;
}
boost::shared_ptr<MyClass> clone_and_change(std::string p_new_name)
{
test_stream()
<< "- In MyClass(\"" << this->m_name << "\").clone_and_change(p_new_name=\""
<< p_new_name << "\")" << std::endl;
boost::shared_ptr<MyClass> result(new MyClass(*this));
result->m_name = p_new_name;
return result;
}
std::string get_name()
{
test_stream() << "- In MyClass(\"" << this->m_name << "\").get_name()" << std::endl;
return this->m_name;
}
std::string m_name;
};
struct ScopePreAndPostActions
{
ScopePreAndPostActions()
{
test_stream() << "[Before action...]" << std::endl;
}
~ScopePreAndPostActions()
{
test_stream() << "[After action...]" << std::endl;
}
};
template <class FuncType_>
struct FuncWrapper;
// You can code-generate specializations for other arities...
template <class R_, class A0_, class A1_>
struct FuncWrapper<R_ (A0_, A1_)>
{
typedef R_ (*func_type)(A0_, A1_);
typedef typename boost::add_const<typename boost::add_reference<typename A0_>::type>::type AC0_;
typedef typename boost::add_const<typename boost::add_reference<typename A1_>::type>::type AC1_;
func_type m_wrapped_func;
FuncWrapper(func_type p_wrapped_func)
: m_wrapped_func(p_wrapped_func)
{
}
R_ operator()(AC0_ p0, AC1_ p1)
{
ScopePreAndPostActions actions_guard;
return this->m_wrapped_func(p0, p1);
}
};
template <
class R_,
class C_,
class A0_=void,
class A1_=void,
class A2_=void
// ...
>
struct MemberFuncWrapper;
template <class R_, class C_, class A0_>
struct MemberFuncWrapper<R_, C_, A0_>
{
typedef R_ (C_::*member_func_type)(A0_);
typedef typename boost::add_const<typename boost::add_reference<typename A0_>::type>::type AC0_;
member_func_type m_wrapped_method;
MemberFuncWrapper(member_func_type p_wrapped_method)
: m_wrapped_method(p_wrapped_method)
{
}
R_ operator()(C_* p_self, AC0_ p0)
{
ScopePreAndPostActions actions_guard;
return (p_self->*(this->m_wrapped_method))(p0);
return R_();
}
};
namespace boost { namespace python { namespace detail {
// You can code-generate specializations for other arities...
template <class R_, class P0_, class P1_>
inline boost::mpl::vector<R_, P0_, P1_>
get_signature(FuncWrapper<R_ (P0_, P1_)>, void* = 0)
{
return boost::mpl::vector<R_, P0_, P1_>();
}
template <class R_, class C_, class P0_>
inline boost::mpl::vector<R_, C_*, P0_>
get_signature(MemberFuncWrapper<R_, C_, P0_>, void* = 0)
{
return boost::mpl::vector<R_, C_*, P0_>();
}
} } }
// -------------------------------------------------------------------
template <class FuncPtr_>
void make_wrapper(FuncPtr_);
// You can code-generate specializations for other arities...
template <class R_, class A0_, class A1_>
FuncWrapper<R_ (A0_, A1_)> make_wrapper(R_ (*p_wrapped_func)(A0_, A1_))
{
return FuncWrapper<R_ (A0_, A1_)>(p_wrapped_func);
}
template <class R_, class C_, class A0_>
MemberFuncWrapper<R_, C_, A0_> make_wrapper(R_ (C_::*p_wrapped_method)(A0_))
{
return MemberFuncWrapper<R_, C_, A0_>(p_wrapped_method);
}
template <class R_, class C_, class A0_, class A1_>
MemberFuncWrapper<R_, C_, A0_, A1_> make_wrapper(R_ (C_::*p_wrapped_method)(A0_, A1_))
{
return MemberFuncWrapper<R_, C_, A0_, A1_>(p_wrapped_method);
}
using namespace boost::python;
void RegisterTestWrapper()
{
def("GetValueAndClearTestStream", &get_value_and_clear_test_stream);
def("TestFunc", &func);
def(
"TestWrappedFunctor",
make_wrapper(&func)
);
{
class_< MyClass, shared_ptr<MyClass>, boost::noncopyable > c("MyClass", init<std::string>());
c.def("CloneAndChange", &MyClass::clone_and_change);
c.def("GetName", &MyClass::get_name);
c.def("WrappedCloneAndChange", make_wrapper(&MyClass::clone_and_change));
}
}
And on python:
import unittest
from _test_wrapper import GetValueAndClearTestStream, TestFunc, TestWrappedFunctor, MyClass
class Test(unittest.TestCase):
def setUp(self):
GetValueAndClearTestStream()
def testWrapper(self):
self.assertEqual(TestFunc(69, 1.618), 'func(a=69, b=1.618)')
self.assertEqual(GetValueAndClearTestStream(), '- In func(a=69, b=1.618)\n')
self.assertEqual(TestWrappedFunctor(69, 1.618), 'func(a=69, b=1.618)')
self.assertEqual(
GetValueAndClearTestStream(),
(
'[Before action...]\n'
'- In func(a=69, b=1.618)\n'
'[After action...]\n'
),
)
def testWrappedMemberFunction(self):
from textwrap import dedent
x = MyClass("xx")
y = x.WrappedCloneAndChange("yy")
z = y.WrappedCloneAndChange("zz")
self.assertEqual(x.GetName(), "xx")
self.assertEqual(y.GetName(), "yy")
self.assertEqual(z.GetName(), "zz")
self.assertEqual(
GetValueAndClearTestStream(),
dedent('''\
- In MyClass::MyClass(p_name="xx")
[Before action...]
- In MyClass("xx").clone_and_change(p_new_name="yy")
- In MyClass::MyClass(p_another=MyClass("xx"))
[After action...]
[Before action...]
- In MyClass("yy").clone_and_change(p_new_name="zz")
- In MyClass::MyClass(p_another=MyClass("yy"))
[After action...]
- In MyClass("xx").get_name()
- In MyClass("yy").get_name()
- In MyClass("zz").get_name()
'''),
)
Have you looked at the function wrapping technique described by Stroustrup in his "Wrapping C++ Member Function Calls" paper? There's also a SO response here that demonstrates how to implement it in a concise manner. Basically you'd implement a template that overloads operator->(). Within that operator's implementation you'd construct a temporary object before your actual function call. The temporary object's constructor and destructor take care of invoking your "pre-" and "post-" code before and after your actual function call, respectively.