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.