Home > Mobile >  How can I enable a constexpr `missing return statement` active error without warnings?
How can I enable a constexpr `missing return statement` active error without warnings?

Time:05-11

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

  • Related