I have the following third-party API:
using StatisticsFunc = double (*)(const std::vector<double> &)
libraryClass::ComputeStatistics(StatisticsFunc sf);
Which I'm using like this:
obj1->ComputeStatistics([](const auto& v) {return histogram("obj1", v);};
obj2->ComputeStatistics([](const auto& v) {return histogram("obj2", v);};
But all those lambdas are just repeated code. I'd rather have it like this:
obj1->ComputeStatistics(getHistogramLambda("obj1"));
So I need to define:
constexpr auto getHistogramLambda(const char* name) {
return [](const auto& v) {return histogram(name, v);};
}
But it won't work, because name
is not captured. Neither will this work:
constexpr auto getHistogramLambda(const char* name) {
return [name](const auto& v) {return histogram(name, v);};
}
Because capturing lambda is not stateless anymore and cannot be cast to function pointer.
Ofc one can do it as a macro, but I want a modern C++ 17 solution.
Passing string as template argument seems an option as well:
https://stackoverflow.com/a/28209546/7432927 , but I'm curious if there's a constexpr
way of doing it.
C++'s constexpr brings another new dimension to the problem too! It behaves like const in the sense that it makes all pointers constant pointers. But because it occurs at the start of your statement (rather than after the '*') its not immediately obvious.
The easiest way to check whether a function (e.g., foo ) is constexpr is to assign its return value to a constexpr as below: constexpr auto i = foo(); if the returned value is not constexpr compilation will fail.
Constexpr constructors are permitted for classes that aren't literal types. For example, the default constructor of std::unique_ptr is constexpr, allowing constant initialization.
Sort of.
This:
obj1->ComputeStatistics(getHistogramLambda("obj1"));
Won't work for the reasons you point out - you need to capture state. And then, we can't write this:
obj1->ComputeStatistics(getHistogramLambda<"obj1">());
Because while we can have template parameters of type const char*
we can't have them bind to string literals. You could do it this way:
template <const char* name>
constexpr auto getHistogramLambda() {
return [](const auto& v) {return histogram(name, v);};
}
const char p[] = "obj1";
obj1->ComputeStatistics(getHistogramLambda<p>());
Which is pretty awkward because you need to introduce the extra variable for each invocation. In C++20, we'll be able to write a class type that has as its template paramater a fixed string, which will allow getHistogramLambda<"obj1">
to work, just in a slightly different way.
Until then, the best way currently is probably to use a UDL to capture the individual characters as template parameters of some class type:
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));
The intent here is that "obj"_udl
is an object of type X<'o', 'b', 'j', '1'>
- and then we reconstruct the string within the body of the function template in a way that still does not require capture.
Is this worth it to avoid the duplication? Maybe.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With