I have a templated function that invokes another function and stores its return value, then does some work before returning the value. I'd like to extend this to handle T = void
, and was wondering if specialization is my only option.
template<typename T>
T Foo( T(*Func)() )
{
// do something first (e.g. some setup)
T result = Func();
// do something after (e.g. some tear down)
return result;
}
// Is this specialization the only option?
template<>
void Foo<void>( void(*Func)() )
{
// do something first (e.g. some setup)
Func();
// do something after (e.g. some tear down)
return;
}
void Bar() {}
int BarInt() { return 1; }
int main()
{
Foo<int>(&BarInt);
Foo<void>(&Bar);
}
Or can the regular version of Foo
be modified to handle the void
type and basically do nothing in that case? I was thinking that maybe my local result could be wrapped in a type that could handle void
maybe, but could also see the assignment as being a deal-breaker.
Given that your operation does not depend on the result of the function, you can do it without a specialization. It is ok for a function returning void
to return an expression of type void
. So the return
part is not the troublesome one, but you need to figure out a way to do the pre and post operations. Constructors and destructors will help you there:
struct do_something_helper
{
do_something_helper()
{
// do something first (e.g. take a lock)
}
~do_something_helper()
{
// do something after (e.g. release a lock)
}
};
Then you can write your function like this:
template<typename T>
T Foo( T(*Func)() )
{
do_something_helper _dummy_helper; // constructor called here
return Func();
// destructor called here
}
For a more general solution using lambdas as you commented, it could look like this:
template< typename Pre, typename Post >
struct scope_guard
{
scope_guard( Pre&& pre, Post&& post )
: _post( std::forward< Post >( post ) )
{
pre();
}
~scope_guard()
{
_post();
}
Post _post;
};
template< typename Pre, typename Post >
scope_guard< Pre, Post > make_scope_guard( Pre&& pre, Post&& post )
{
return scope_guard< Pre, Post >( std::forward< Pre >( pre ), std::forward< Post >( post ) );
}
template<typename T>
T Foo( T(*Func)() )
{
auto do_something_helper =
make_scope_guard(
[](){ /* do something first (e.g. take a lock) */ },
[](){ /* do something after (e.g. release a lock) */ }
);
return Func();
}
A type-erased version using std::function< void() >
would be easier to write and use, but it would be rather inefficient.
If all you're doing is taking and releasing a lock then you should use RAII instead of calling lock/unlock functions. This is especially true if Func
could throw, as if it does the code after it will not be called.
Once you have an RAII lock (or some time of RAII object if you need more than locks), you can simply do this which works for void
:
template<typename T>
T Foo( T(*Func)() )
{
lock my_lock;
return Func();
}
template<typename T>
T Foo( T(*Func)() )
{
struct raii_wrapper {
raii_wrapper(T(*Func)()) : Func(Func) {
// pre effects
}
~raii_wrapper() {
// post effects
}
T(*Func)();
} actions(Func);
return Func;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With