4

today I'm struggling with C++ templates. This is my simple code for convenient exception handling in JNI code.

template<typename T>
std::optional<T> handleNativeCrash(JNIEnv *env, std::function<T(void)> f) {
    try {
        return std::optional<T>(f());
    }
    catch (const std::exception &e) {
        jniThrowException(env, e.what());
        return {};
    }
}

When I try to use it without specifying T, Clang fails roughly with this error

no matching function for call to 'handleNativeCrash'
      return my_namespace::handleNativeCrash(env, [&]{
             ^~~~~~~~~~~~~~~~~~~~~
  /......../jni.cpp:116:39)'
      std::optional<T> handleNativeCrash(JNIEnv *env, std::function<T(void)> f) {
                       ^
  1 error generated.

I would like to infer T automatically from lambda return type. I tried to write deduction guide, but it seems that I'm unable to write it for global function. I tried to create simple template struct which contained only this function, but I failed too. It seems I don't really understand C++ template metaprogramming.

My first attempt was like this, but Clang just crashed complaining about illegal syntax and printing it's backtrace. I will report bug soon, but I need to finish this job first.

template<typename T>
handleNativeCrash(JNIEnv *env, std::function<T(void)> f) -> handleNativeCrash<decltype(f())>;

How can I achieve my goal?

2
  • 2
    A lambda is not a std::function. Better use a generic Callable type. Commented Feb 1, 2019 at 12:43
  • About your clang crash: clang is right it's illegal (missing auto), wrong to crash. Commented Feb 1, 2019 at 12:49

2 Answers 2

9

You cannot use template deduction for that, it's not smart enough and only works on matching.

But you can manually infer it:

template<class Callable>
auto handleNativeCrash(JNIEnv *env, Callable f)
-> std::optional<decltype(f())>
{
    try {
        return f();
    }
    catch (const std::exception &e) {
        jniThrowException(env, e.what());
        return {};
    }
}

Simplified live demo

Sign up to request clarification or add additional context in comments.

2 Comments

Why not use automatic return-type-deduction? Do you actually want to use SFINAE?
@dedup that's an option but basically I didn't want to hide the return type (an noptional) inside the function impl.
0

The problem is that a lambda can be converted to a std::function but isn't a std::function.

So the T type can't be deduced because you don't have a std::function and a std::function can't be obtained because you don't know T.

A sort of chicken and egg problem.

So you have to pass a std::function to handleNativeCrash()

return my_namespace::handleNativeCrash(env, std::function{[&]{/*...*/}});

or you have to receive a generic callable as in YSC answer or, by example, something as follows (thanks to Holt for pointing my original error):

template <typename R = void, typename F,
          typename T = std::conditional_t<std::is_same_v<void, R>,
                          decltype(std::declval<F>()()), R>>
std::optional<T> handleNativeCrash(JNIEnv *env, F f) {
    try {
        return std::optional<T>(f());
    }
    catch (const std::exception &e) {
        jniThrowException(env, e.what());
        return {};
    }
}

The YSC solution (detecting returned type using decltype() in trailing return type) impose the type returned by the functional; using additionals (with default values) template parameters permit also to "hijack" the returned type explicating it.

Suppose you have a l lambda that return an int but you want a std::optional<long>, you can write something as

return my_namespace::handleNativeCrash<long>(env, l);

2 Comments

You cannot put T before F since the default value for T depends on F so you cannot specify only long in your last example.
@Holt - D'oh! You're right. I have to make it a little more complicated. Corrected; thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.