Suppose I have constexpr
data set. I mostly use it at compile time, it stores data for settings that can affect future constexpr
statements. The string_view
allows me to name these settings and search for them easily:
struct data {
std::string_view name;
int value;
};
constexpr auto data_set = std::array{
data{"foo", 100},
data{"bar", 200},
};
and this is the constexpr
function that allows me to find data points and return their value:
constexpr int get_data(std::string_view search) {
for (auto& data : data_set) {
if (data.name == search) {
return data.value;
}
}
}
this works nicely, when I call this function with correct string names and incorrect ones. Because if it doesn't find name
, the function has no return statement and therefore fails to initialize a constexpr
value:
int main() {
constexpr auto foo = get_data("foo");
constexpr auto foo2 = get_data("foo2"); //Error C2131: expression did not evaluate to a constant
}
and my IDE (Visual Studio) even gives me a nice visible active error:
Error (active) E0028 expression must have a constant value
however, while using this and having non-constexpr calls to it, I always get the warning
warning C4715: 'get_data': not all control paths return a value
and obviously I can't add a default return value, as that defeats the "constexpr
fails, if invalid" purpose, but I still would like to be able to initialize run time variables with it (as the search string_view is still known at compile time making this safe).
So is there a way to keep this error for invalid inputs, but remove the error? And can you also make this work for functions that return nothing?
CodePudding user response:
consteval
will enable this behaviour. It makes it so, that any function marked this way, has to be evaluated at compile time. This will also remove the warning, as it will be replaced by an error if it didn't evaluate properly.
consteval int get_data(std::string_view search) {
for (auto& data : data_set) {
if (data.name == search) {
return data.value;
}
}
}
int main() {
constexpr auto foo = get_data("foo");
constexpr auto foo2 = get_data("foo2");
}
the "foo2"
line results in C2131 and active error E3133:
Error C2131 expression did not evaluate to a constant
Error (active) E3133 call to consteval function "get_data" did not produce a valid constant expression
you can still use this to initialize run-time variables:
auto bar = get_data("bar");
as get_data
can be evaluated at compile time, and is basically replaced by data{ "bar", 200 }.value;
, or just 200
.
now consteval
will make it so that that pure runtime calls no longer work (like reading the search term from std::cin
input or from a file) but that is desired behavior, as that could theoretically result in the unspecified branch if the input is invalid.
can you also make this work for functions that return nothing?
it is possible to add statements that are invalid in the branch that is supposed to fail, if reached. For example 1 / 0
:
consteval void check_data(std::string_view search) {
for (auto& data : data_set) {
if (data.name == search) {
return;
}
}
auto invalid = 1 / 0;
}
int main() {
check_data("foo");
check_data("bar");
check_data("bar2"); //E3133 active error
}
@GoswinvonBrederlow suggested throwing exceptions, this also works very well, even cleaner then 1 / 0
in my opinion:
consteval auto get_data(std::string_view search) {
for (auto& data : data_set) {
if (data.name == search) {
return data.value;
}
}
throw std::exception("fail");
}
consteval void check_data(std::string_view search) {
for (auto& data : data_set) {
if (data.name == search) {
return;
}
}
throw std::exception("fail");
}
int main() {
constexpr auto foo = get_data("foo");
constexpr auto foo2 = get_data("foo2"); //E3133 active error
auto bar2 = get_data("foo2"); //E3133 active error
check_data("foo");
check_data("foo2"); //E3133 active error
}
CodePudding user response:
I know there can be many elegant solutions but what I'd suggest is as simple as adding an unconditional return to the end of your function.
constexpr int get_data(std::string_view search) {
for (auto& data : data_set) {
if (data.name == search) {
return data.value;
}
}
return -1;
}
See it running: https://godbolt.org/z/zKx7M3j8a