2

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.

Alex
  • 2,145
  • 6
  • 36
  • 72
  • `std::pow` and the functions `std::pow` calls have to be `constexpr` Examine the std source for the functions. You might be able to substitute from a lib that does. Alternately, just initialize the statics at run time. – doug Oct 28 '19 at 04:04
  • There's an older question stating that the standard doesnt allow them to be `constexpr`, at least in C++14. Has this changed in C++17? https://stackoverflow.com/questions/27744079/is-it-a-conforming-compiler-extension-to-treat-non-constexpr-standard-library-fu – Michael Anderson Oct 28 '19 at 04:08
  • @MichaelAnderson I don't believe it has in C++17. No obvious reason it couldn't be but wouldn't be standard. – doug Oct 28 '19 at 04:09
  • Also another similar questions: https://stackoverflow.com/questions/50477974/constexpr-exp-log-pow , https://stackoverflow.com/questions/17347935/constexpr-math-functions – Michael Anderson Oct 28 '19 at 04:09
  • @MichaelAnderson So the problem comes down to breaking existing code that uses `errno` Grr. – doug Oct 28 '19 at 04:11
  • @doug what would need to be changed to initialize the statics at run time? Apologies, I'm a c++ novice and I'm not trying to break code. – Alex Oct 28 '19 at 06:11
  • Just search for "initializing statics" for C++ on this site. Lots of options. IMO, the easiest is to declare them in a global struct and call whatever functions you need in the struct'sdefault constructor. – doug Oct 28 '19 at 16:44
  • @doug I'm probably completely missing what is "correct" but doing something like `static const double` instead of `static constexpr double` gives me the error: `error: in-class initializer for static data member of type 'const double' requires 'constexpr' specifier [-Wstatic-float-init]`.. what is the correct way to initialize a static variable in the header file? – Alex Oct 29 '19 at 05:49

1 Answers1

4

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
};
doug
  • 3,840
  • 1
  • 14
  • 18
  • Thanks for this! The only issue I see with the `constexpr` function is that `while (--exp)` is invalid with the following error: `error: statement not allowed in constexpr function while (--exp)` – Alex Oct 29 '19 at 22:26
  • @Alex What compiler and settings are you using? Works with clang and gcc here: https://godbolt.org/z/UvjyGI By any chance did you declare exp a double? Only works with integer exponents since doubles will require other math functions with similar issues of not being constexpr. – doug Oct 30 '19 at 05:42