Ryan Stortz
Ryan Stortz

Reputation: 11

Catching and wrapping exceptions with boost::optional

I'd like to catch exceptions generated by library code and wrap them in boost::optionals (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

Answers (1)

Marshall Clow
Marshall Clow

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

Related Questions