Reputation: 11
I'd like to catch exceptions generated by library code and wrap them in boost::optional
s (or std::experimental::optional
). My code works for the trivial cases, but has difficulty deducing the correct type when calling overloaded functions. I've reduced my test case to this:
// Compile with: clang++ -std=c++14 -stdlib=libc++ -Wall -Wshadow -Wextra -o except_to_optional except_to_optional.cpp
#include <iostream>
#include <boost/optional.hpp>
template<typename Func, typename... Args>
auto try_call(Func f, Args&&... args) noexcept -> boost::optional<std::decay_t<decltype(f(std::forward<Args>(args)...))>>
{
using RT = std::decay_t<decltype(f(std::forward<Args>(args)...))>;
try {
return boost::make_optional<RT>(f(std::forward<Args>(args)...));
} catch(...) {
return boost::none;
}
}
int func1(char * ptr)
{
if (!ptr)
throw 14;
return strlen(ptr);
}
int func1(char * ptr, size_t len)
{
if (!ptr)
throw 14;
const size_t calc_len = strlen(ptr);
return calc_len < len ? calc_len : len;
}
int main(int argc, char **argv)
{
char *omg = "omg";
#if 1
// This is the desired syntax, but it fails
auto val = try_call(func1, omg);
#else
// This works, but its ugly
int (*f)(char *) = func1;
auto val = try_call(f, omg);
#endif
if (val)
std::cout << omg << " has a length of " << *val << "\n";
else
std::cout << "Something went wrong...\n";
return 0;
}
When attempting to compile the "broken" example, I get the following output:
except_to_optional.cpp:50:14: error: no matching function for call to 'try_call'
auto val = try_call(func1, omg);
^~~~~~~~
except_to_optional.cpp:17:6: note: candidate template ignored: couldn't infer template argument 'Func'
auto try_call(Func f, Args&&... args) noexcept -> boost::optional<std::decay_t<decltype(f(std::forward<Args>(args)...))>>
^
I'm able to side-step the issue by creating a function pointer with the correct type, but this isn't ideal. Is there a solution to my problem or am I stuck with the ugly function pointer hack?
Cheers, Ryan
Upvotes: 1
Views: 950
Reputation: 16660
In the first case, there are two different versions of func1
that you might want to pass. Thee compiler can't figure out what the type of func1
that it needs to instantiate the template, so it doesn't get to the point of "looking inside" the template to see that the 'one parameter' version is the right one.
That's what the compiler error message is telling you.
In the second case, you're picking one overload of func1
and passing it to try_call
. There's no ambiguity, so the compiler can figure out what the signature to apply to the template.
Upvotes: 2