Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrap arbitrary function call via template metaprogramming with try..catch block in modern C++

I want to create some template which essentially should wrap it's parameter. The parameter should be an arbitrary function call, which gets wrapped via some template metaprogramming magic with prefix and postfix code.

I want to use it like follows:

auto result = try_call( some_vector.at(13) );

and try_call would be defined somehow that it wraps a try..catch block around some_vector.at(13). Something like this:

template<typename T>
// some template metaprogramming magic here
try {
    auto value = // execute the parameter here, i.e. some_vector.at(13);
    return std::experimental::optional<T>(value);
} 
catch (std::exception&) {
    return std::experimental::nullopt;
}

There is that paper of Bjarne Stroustrup, but that's not exactly describing what I need, and I wasn't able to find a solution to this problem.

If this isn't possible directly, I am currently thinking of doing it via a templated function taking a lambda:

template<typename Func>
auto try_call(Func f) {
    try {
        return f();
    } catch(std::exception&) {
        return std::experimental::nullopt;
    }
}

But I don't know if that's a good idea. The lambda has some overhead, I guess? I want to avoid any unneccessary overhead.

like image 509
j00hi Avatar asked Jul 20 '15 21:07

j00hi


1 Answers

Actually, your solution with a lambda is quite good and efficient. From a type theoretic point of view, try_call is a higher order function: It takes as argument another function and executes it in a try catch context.

template<typename Func>
auto try_call(Func f) -> std::experimental::optional<std::decay_t<decltype(f())>> {
    try {
        return std::experimental::make_optional(f());
    } catch(std::exception&) {
        return std::experimental::nullopt;
    }
}

Calling it with a lambda will yield what you want without any overhead. A lambda is compiled to an anonymous struct with an overloaded function call operator. This struct is used as template parameter for your try_call function. Therefore, the compiler knows exactly the function to be executed when calling f() and it will be inlined. No overhead involved.

like image 173
gexicide Avatar answered Sep 22 '22 23:09

gexicide