Search code examples
c++c++20constexprconstexpr-function

Using constexpr for both runtime and compile-time evaluations - passing constexpr value as a function parameter


I'm trying to make a constexpr function which will run both at compile and runtime depending on the call.The function will check a unsigned long and will return a number depending on it's value. Parameter can be either known at compile time or a variable that's why I cannot use consteval.

constexpr std::uint32_t GetFlagFromFlagName(unsigned long FlagName)
{

    if (!std::is_constant_evaluated())
    {
        std::cout << "Running function at runtime" << '\n';
    }


    switch (FlagName)
    {
    case 0:
        return 10;
    case 1:
        return 20;
    default:
        return 0;
    }
}


void MyTestFunc(std::uint32_t test)
{
    std::cout << "test>>>" << test << "\n";
}

int main()
{
    constexpr auto CompileTimeVariable = GetFlagFromFlagName(0); // Should be Compile-time
    std::cout << "CompileTimeVariable result>>>" << CompileTimeVariable << "\n";

    MyTestFunc(GetFlagFromFlagName(0)); // Not compile time

    system("pause");
}

result is:

CompileTimeVariable result>>>10 
Running function at runtime 
test>>>10

Expected output:( std::cout << "Running function at runtime" << '\n'; never runs)

CompileTimeVariable result>>>10 
test>>>10

Second call of GetFlagFromFlagName is evaluated at runtime but I expect it to be evaluated at compile-time becaused I passed it a compile-time variable 0.

I understand that in order to make a variable evaluate at at compile time I should make it constexpr. Is there a way to pass result of GetFlagFromFlagName without declaring it a separate variable before?Casting a function result to constexpr doesn't work:

MyTestFunc((constexpr)GetFlagFromFlagName(0));


Solution

  • You misunderstand when something is evaluated as a constant expression. GetFlagFromName(0) receives 0 as an argument, which is a constant expression, and could be constant-evaluated.

    However constant evaluation doesn't take place whenever possible, or whenever the compiler feels like it. It only takes place in specific contexts, such as

    • array sizes
    • template arguments
    • initializers of constexpr variables
    • ...

    See also When does a constexpr function get evaluated at compile time?

    If you want to force constant-evaluation of GetFlagFromName, see single expression helper for compile-time enforced constexpr function evaluation possible?. You would write something like:

    MyTestFunc(CONSTEVAL(GetFlagFromFlagName(0)));
    

    If GetFlagFromName is never evaluated at run-time, you could also just make it consteval which forces constant evaluation in every context. However, that doesn't seem to be the case.

    Last but not least, if the FlagName parameter is always meant to be a constant expression, you could turn it into a template parameter:

    template <unsigned long FlagName>
    constexpr std::uint32_t GetFlagFromFlagName()