Определяемое пользователем руководство по выводу лямбды - PullRequest
0 голосов
/ 01 февраля 2019

сегодня я борюсь с шаблонами C ++.Это мой простой код для удобной обработки исключений в коде JNI.

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 {};
    }
}

Когда я пытаюсь использовать его без указания T, Clang примерно завершается с ошибкой

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хотел бы вывести T автоматически из типа возврата лямбда.Я пытался написать руководство по выводу, но мне кажется, что я не могу написать его для глобальной функции.Я пытался создать простую структуру шаблона, которая содержала бы только эту функцию, но у меня тоже не получилось.Кажется, я не совсем понимаю метапрограммирование шаблона C ++.

Моя первая попытка была такой, но Clang просто потерпел неудачу, жалуясь на неправильный синтаксис и печатая его трассировку.Я скоро сообщу об ошибке, но сначала мне нужно закончить эту работу.

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

Как мне достичь своей цели?

Ответы [ 2 ]

0 голосов
/ 01 февраля 2019

Проблема в том, что лямбда может быть преобразована в std::function, но не std::function.

Так что тип T не может быть выведен, потому что у вас нетstd::function и std::function невозможно получить, потому что вы не знаете T.

Этакая проблема с курицей и яйцом.

Так что вам нужно пройти std::function до handleNativeCrash()

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

или вы должны получить общий вызываемый элемент, как в ответе YSC, или, например, что-то следующее (спасибо Холту за указание на мою первоначальную ошибку):

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 {};
    }
}

Решение YSC (обнаружение возвращаемого типа с использованием decltype() в конце типа возврата) навязывает тип, возвращаемый функционалом;использование дополнительных (со значениями по умолчанию) параметров шаблона позволяет также «перехватывать» возвращаемый тип, объясняя его.

Предположим, у вас есть l лямбда, которая возвращает int, но вы хотите std::optional<long>, выможно написать что-то как

return my_namespace::handleNativeCrash<long>(env, l);
0 голосов
/ 01 февраля 2019

Вы не можете использовать шаблонное вычитание для этого, оно недостаточно умное и работает только при сопоставлении.

Но вы можете вывести его вручную:

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 {};
    }
}

Упрощенная живая демонстрация

...