1

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

  • Wrap overloaded function names in perfect forwarding lambdas. – Yakk - Adam Nevraumont Oct 18 '16 at 01:48
  • Offtopic: You may find outcome in proposed Boost.Outcome (https://github.com/ned14/boost.outcome) much nicer than optional for preserving error state whilst also returning a T. Has the same API as an optional too. One can also convert most exceptions into an equivalent result (i.e. std::error_code). – Niall Douglas Oct 18 '16 at 13:18

1 Answers1

2

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.

Marshall Clow
  • 15,972
  • 2
  • 29
  • 45
  • 1
    I think the actual question was "*Is there a solution to my problem or am I stuck with the ugly function pointer hack?*" AFAIK Boost.Bind manages such that no disambiguation from the user is needed (overloading for function-pointers vs. other callables I'd guess?). – ildjarn Oct 18 '16 at 02:30
  • I don't see a solution off the top of my head, but that may be just that it's late at night, and I've been up since 0500. – Marshall Clow Oct 18 '16 at 05:53