c++templatesc++11

handling void type in template


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.


Solution

  • 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.