указатель на параметризованную функцию constexpr - PullRequest
0 голосов
/ 23 января 2019

У меня есть следующий сторонний API:

using StatisticsFunc = double (*)(const std::vector<double> &)
libraryClass::ComputeStatistics(StatisticsFunc sf);

который я использую вот так:

obj1->ComputeStatistics([](const auto& v) {return histogram("obj1", v);};
obj2->ComputeStatistics([](const auto& v) {return histogram("obj2", v);};

Но все эти лямбды - просто повторяющийся код. Я бы предпочел, чтобы это было так:

obj1->ComputeStatistics(getHistogramLambda("obj1"));

Так что мне нужно определить:

constexpr auto getHistogramLambda(const char* name) {
    return [](const auto& v) {return histogram(name, v);};
}

Но это не сработает, потому что name не захвачено. Это также не сработает:

constexpr auto getHistogramLambda(const char* name) {
    return [name](const auto& v) {return histogram(name, v);};
}

Поскольку захват лямбды больше не является состоянием состояния и не может быть приведен к указателю на функцию.

Конечно, это можно сделать как макрос, но я хочу современное решение C ++ 17.

Передача строки в качестве аргумента шаблона также представляется опцией: https://stackoverflow.com/a/28209546/7432927, но мне любопытно, если есть constexpr способ сделать это.

Ответы [ 3 ]

0 голосов
/ 23 января 2019

Не уверен, что понимаю, что именно вам нужно, но ... как насчет объявления глобального constexpr массива char const указателей

constexpr std::array<char const *, 3u> arrStr {{"obj0", "obj1", "obj2"}};

затем получить в getHistogramLambda() индекс требуемой строки в качестве параметра шаблона?

template <std::size_t N>
constexpr auto getHistogramLambda () {
    return [](const auto& v) {return histogram(arrStr.at(N), v);};
}

Таким образом, вы можете позвонить ComputeStatistic() следующим образом

obj1->ComputeStatistics(getHistogramLambda<1u>());
0 голосов
/ 24 января 2019

Другой ответ, любезно предоставлено Майкл Парк . Мы можем закодировать нужное нам значение в типе - не передавая строковый литерал, который нам нужен, в качестве аргумента функции или аргумента шаблона, а как фактический тип - и таким образом нам не нужно его захватывать:

#define CONSTANT(...) \
  union { static constexpr auto value() { return __VA_ARGS__; } }
#define CONSTANT_VALUE(...) \
  [] { using R = CONSTANT(__VA_ARGS__); return R{}; }()


template <typename X>
constexpr auto getHistogramLambda(X) {
    return [](const auto& v) { return histogram(X::value(), v);};
}

obj->ComputeStatistic(getHistogramLambda(CONSTANT_VALUE("obj1")));
obj->ComputeStatistic(getHistogramLambda(CONSTANT_VALUE("obj2")));

Не уверен, что это лучше, чем подход UDL в данном конкретном случае, но это, безусловно, интересная техника.

0 голосов
/ 23 января 2019

Сортировка.

Это:

obj1->ComputeStatistics(getHistogramLambda("obj1"));

Не сработает по указанным вами причинам - вам нужно зафиксировать состояние. И потом, мы не можем написать это:

obj1->ComputeStatistics(getHistogramLambda<"obj1">());

Поскольку у нас могут быть параметры шаблона типа const char*, мы не можем связывать их со строковыми литералами. Вы могли бы сделать это так:

template <const char* name>
constexpr auto getHistogramLambda() {
    return [](const auto& v) {return histogram(name, v);};
}

const char p[] = "obj1";
obj1->ComputeStatistics(getHistogramLambda<p>());

Что довольно неудобно, потому что вам нужно вводить дополнительную переменную для каждого вызова. В C ++ 20 мы сможем написать тип класса, который имеет в качестве параметра-шаблона фиксированную строку, которая позволит getHistogramLambda<"obj1"> работать, немного по-другому.

До тех пор лучшим способом в настоящее время, вероятно, является использование UDL для захвата отдельных символов в качестве параметров шаблона некоторого типа класса:

template <char... Cs>
constexpr auto getHistogramLambda(X<Cs...>) {
    static constexpr char name[] = {Cs..., '\0'};
    return [](const auto& v) { return histogram(name, v);};
}


obj->ComputeStatistic(getHistogramLambda("obj1"_udl));

Намерение здесь состоит в том, что "obj"_udl является объектом типа X<'o', 'b', 'j', '1'> - и затем мы восстанавливаем строку в теле шаблона функции таким образом, что по-прежнему не требуется захват.

Стоит ли этого избегать дублирования? Может быть.

...