Alex
Alex

Reputation: 2299

Getting constexpr to work with pow in C++17 on OSX

I'm working on trying to get a Linux based project, written in C++17 to work on OSX (Mojave). Most everything compiles just fine, until I get to this file: ClassName.hpp:

class ClassName {

public:

    static constexpr double DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1 = 2; // represents 0.99
    static constexpr double DEFAULT_TARGET_TFINAL_DIGITS_FROM_0 = 10; // represents 1e-10
    static constexpr double DEFAULT_TARGET_INITIAL_PBAD = (1-pow(10,-DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1));
    static constexpr double DEFAULT_TARGET_FINAL_PBAD = pow(10,-DEFAULT_TARGET_TFINAL_DIGITS_FROM_0);
    static constexpr double DEFAULT_ERROR_TOL_DIGITS = 0.9; // as a fraction of digits in the last place from the above.
    static constexpr double DEFAULT_SAMPLE_TIME = 1;

    // more unrelated code
};

When compiling this I get the following error:

error: constexpr variable
     'DEFAULT_TARGET_INITIAL_PBAD' must be initialized by a constant expression
 ...double DEFAULT_TARGET_INITIAL_PBAD = (1-pow(10,-DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1));
           ^                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ClassName.hpp: note: non-constexpr function 'pow<int, double>'
     cannot be used in a constant expression
   static constexpr double DEFAULT_TARGET_INITIAL_PBAD = (1-pow(10,-DEFAULT_TARGET_TINITI...
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/math.h:968:1: note:
     declared here
pow(_A1 __lcpp_x, _A2 __lcpp_y) _NOEXCEPT

So for some reason this works on Ubuntu and CentOS. I think it has to do with how pow is defined? But I'm not sure how to fix it, or if that is even the issue. I've also tried removing constexpr from DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1 and DEFAULT_TARGET_TFINAL_DIGITS_FROM_0 and making them const but still run into the same issue.

Upvotes: 2

Views: 2784

Answers (1)

doug
doug

Reputation: 4289

First, you can't initialize constexpr class members with functions that aren't constexpr and std::pow isn't constepxr in standard C++17 . The workaround is to declare them const. While they can't be used in places that require a compile time const they are immutable. A traditional approach is declaring them in header which you include as needed in source files. Then you hneed one implementation file that defines the static const members.

If your code requires compile time const's or constexpr your only option is to write your own pow.

Here's one way to initialize const statics with functions that are not constexpr prior to executing the main() using a portion of your question that shows the technique:

Create a header, constinit.h, that declares the class

// include header guards
// declare the static consts
struct ClassName {
    static double DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1; // represents 0.99
    static double DEFAULT_TARGET_INITIAL_PBAD; // to be initialized by pow
};

Create an implementation file that initializes the statics:

#include "constinit.h"
#include <cmath>

double ClassName::DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1{ 2 }; // represents 0.99
double ClassName::DEFAULT_TARGET_INITIAL_PBAD = (1 - std::pow(10, -DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1));

To use the statics:

#include <iostream>
#include "constinit.h"


int main()
{
    std::cout << ClassName::DEFAULT_TARGET_INITIAL_PBAD << std::endl;
}

If constexpr for compile time initialization is required you need to define your own constexpr pow function. This works in C++17:

    #pragma once // or header guards per your preference

constexpr double my_pow(double x, int exp)
{
    int sign = 1;
    if (exp < 0)
    {
        sign = -1;
        exp = -exp;
    }
    if (exp == 0)
        return x < 0 ? -1.0 : 1.0;
    double ret = x;
    while (--exp)
        ret *= x;
    return sign > 0 ? ret : 1.0/ret;
}
class ClassName {
public:
    static constexpr double DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1 = 2; // represents 0.99
    static constexpr double DEFAULT_TARGET_TFINAL_DIGITS_FROM_0 = 10; // represents 1e-10
    static constexpr double DEFAULT_TARGET_INITIAL_PBAD = (1 - my_pow(10, -DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1));
    static constexpr double DEFAULT_TARGET_FINAL_PBAD = my_pow(10, -DEFAULT_TARGET_TFINAL_DIGITS_FROM_0);
    static constexpr double DEFAULT_ERROR_TOL_DIGITS = 0.9; // as a fraction of digits in the last place from the above.
    static constexpr double DEFAULT_SAMPLE_TIME = 1;

    // more unrelated code
};

Upvotes: 4

Related Questions