-3

For an embedded system, I've written a code that's generating a delay based on a compile-time and run-time selected variable. The compile-time version boils down to writting as much NOP instruction as required for reaching the expected CPU cycle count.

The main part is like this:

#define IS_CONSTEXPR(...) __builtin_constant_p(__VA_ARGS__)
#define FORCE_INLINE      __attribute__((always_inline))

template <int a> struct Nop { 
    __attribute__((always_inline)) static void nop() {
      asm __volatile__("nop");
      Nop<a-1>::nop();
    }
};
template <> struct Nop<0> {
    __attribute__((always_inline)) static void nop() {}
};

void bar(int x) { 
    // TODO: Make an asm version of this to avoid depending on compiler optimization
    for (int i = 0; i < x; i++) 
      Nop<1>::nop(); 
}

template <bool e, int T> struct CTorRT
{   
  FORCE_INLINE CTorRT(int) { 
    if (T == 0) return;
    if (T >= 40) bar(T - 10); // Switch to loop based delay now, since it's above the loop calling overhead 
    else Nop<T>::nop(); // Let's generate a bunch of nop!
  }
};

template <int T> struct CTorRT<false, T>
{
    FORCE_INLINE CTorRT(int v) { bar(v); }
};


#define DELAY_CYCLE(X) { CTorRT<IS_CONSTEXPR(X), IS_CONSTEXPR(X) ? X : 0> a(X); }


int main()
{
   int d = 10;
[...]
   DELAY_CYCLE(30);  // Generates 30 NOP 
   DELAY_CYCLE(d);   // Call bar(10) and loop 10 times over a single NOP loop
[...]
}

The code works as expected on all compilers (except for the oldest ones)

Yet, I've trouble understanding why it's not breaking on this part: CTorRT< A, A ? X : 0>. A is a compile time constant (constexpr), yet X can be a dynamic variable, and as such, my understanding is that A ? X : 0 is not a constexpr (even if the result is obviously a constant expression in all cases).

What part of the standard implies that the expression: "constexpr_bool ? variable_or_constexpr : constexpr" is a constexpr ?

xryl669
  • 3,376
  • 24
  • 47
  • Where is that `CTorRT< A, A ? X : 0>`? – florestan Jan 29 '21 at 11:52
  • In the macro "DELAY_CYCLE(X)". `A` contains the "constexpr-ness" of X – xryl669 Jan 29 '21 at 11:57
  • So what? Macros are not code. Are you familiar with how macros work? – Sam Varshavchik Jan 29 '21 at 11:58
  • Basically, it works this way: If X is a constexpr, it's used as a template parameter of CTorRT template. If X is not a constexpr, the template parameter is 0 and X is used in the constructor of CTorRT – xryl669 Jan 29 '21 at 11:58
  • Sure, but this means absolutely nothing, whatosever, in a macro definition. Now, try ***using*** this macro with a non-constant macro, and see if it works. – Sam Varshavchik Jan 29 '21 at 12:00
  • @SamVarshavchik Of course I am. Please read the code again. The DELAY_CYCLE macro is actually generating code, I'm using the macro to avoid repeating X parameter 4 times. The example usage code is provided in the link. It's not actually required that it's called to answer the question, IMHO. – xryl669 Jan 29 '21 at 12:00
  • @xryl669 I see the macro, but I don't know how you use it. Please include a [minimal example](https://stackoverflow.com/help/minimal-reproducible-example). – florestan Jan 29 '21 at 12:02
  • You can click on the link, to see an example. It's not that hard... – xryl669 Jan 29 '21 at 12:03
  • The `DELAY_CYCLE` macro definition, shown above, does not generate any code, whatsoever. Macros don't work this way. When you ***use*** a macro, only ***then*** you are generating code. A good C++ textbook will have a fuller explanation of how macros work. You will be surprised to learn that you can replace the definition of DELAY_CYCLE with a full implementation of The Sieve Of Erathothenes, and this will still not generate a single byte of code. – Sam Varshavchik Jan 29 '21 at 12:04
  • @xryl669 So you mean the people you're asking to spend their time to help you should do these effort rather than yourself? – florestan Jan 29 '21 at 12:05
  • If you clicked on "as expected", as expected, you'd have see the macro instantiated. Yet, I've edited the question so it's being instantiated here too. – xryl669 Jan 29 '21 at 12:05
  • Now, it's a different story. This is why stackoverflow.com's [help] requires a [mre]. The shown code relies on non-standard, compiler-specific, C++ that always produces a constant expression which can be evaluated at compile-time. – Sam Varshavchik Jan 29 '21 at 12:11
  • @SamVarshavchik Can you elaborate as an answer ? What part of the standard says that the behavior of the compiler is implementation specific ? How to do the same thing in a standard compliant way ? – xryl669 Jan 29 '21 at 12:18
  • @xryl669 Thanks. Finally all necessary information on one screen. No need to be rude to people willing to help you, BTW. You simply increase the chance of getting an answer. – florestan Jan 29 '21 at 12:19
  • For example, if I could have used C++20, I could have replaced the IS_CONSTEXPR(X) via a function wrapper over `std::is_constant_evaluated()` so, this part is clear to me. – xryl669 Jan 29 '21 at 12:21
  • Yet, from this [link](https://gcc.gnu.org/legacy-ml/gcc-patches/2011-10/msg01944.html), the gcc's `__builtin_choose_expr` only exists in C code, and the justification seems that it's possible to do the same in C++ in a standard way. I've something working, but I'm not sure if it's the intended way of doing it and in that case, why? – xryl669 Jan 29 '21 at 12:24
  • @xryl669 AFAIK this is the only standard-compliant way, don't think this is possible in c++17 and earlier. – florestan Jan 29 '21 at 12:24
  • Buried somewhere in the standard: all symbols that begin with a double-underscore are reserved for a compiler's own use as is entirely up to the compiler's use for whatever it wants. Here the compiler waves a magic wand and says: if you use it like that you always get a constant expression. Reserved symbols are mentioned in most textbooks. it would take too much time to dig through a 2000 page standard to locate the right clause. – Sam Varshavchik Jan 29 '21 at 12:35

1 Answers1

1

What part of the standard implies that the expression: "constexpr_bool ? variable_or_constexpr : constexpr" is a constexpr ?

No part does, because __builtin_constant_p is not a standard function (or a regular function at all), and that ternary would not normally be a constexpr. You're using the documented behaviour of a compiler extension, not the standard language at all.

The GCC docs specifically address this:

You may also use __builtin_constant_p in initializers for static data. For instance, you can write

static const int table[] = {
    __builtin_constant_p (EXPRESSION) ? (EXPRESSION) : -1,
    /* … */
};

This is an acceptable initializer even if EXPRESSION is not a constant expression ...

That is, GCC effectively implemented ?constexpr just for their own builtin. This doesn't imply there's any way to achieve the same thing except when using such a compiler extension.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • Ok, it's clear. So the question boils down to [how to apply SFINAE on constexpr-ness](https://stackoverflow.com/questions/15232758) in C++ for 2 different implementations (compile time & runtime) – xryl669 Jan 29 '21 at 13:09