Search code examples
c++c++17template-argument-deduction

User-defined deduction guide for lambda


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?


Solution

  • 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