Go Down

Topic: Templates and __builtin-constant_p (Read 1 time) previous topic - next topic

WizenedEE

I'm trying to get code similar to this to compile:
Code: [Select]

template<bool Flag> struct foo {static const bool bar = Flag;};
int main() {
    int a;
    int baz = foo<__builtin_constant_p(a)>::bar;
}

Code: [Select]

minimal.cpp: In function 'int main()':
minimal.cpp:4:42: error: the value of 'a' is not usable in a constant expression
minimal.cpp:3:9: note: 'int a' is not const
minimal.cpp:4:42: note: in template argument for type 'bool'


It should be that, whether or not a is constant, <whether or not a is constant> should be constant --- ie, the output of __builtin_constant_p() should always be constant and able to be used in a template. However, while the output of __builtin_constant_p(__builtin_constant_p(a)) is always 1 no matter what the type of a, I still get the above error.

Does anyone have a solution for this so that I can determine whether or not to use a template depending on if a value is constant and thus is legal to use a template?

PGT


PaulS

Code: [Select]
    int a;
Local variables are not initialized, unless you provide an initial value.

Code: [Select]
    int baz = foo<__builtin_constant_p(a)>::bar;
Why would you pass an uninitialized variable to any kind of function, constant or not?

Nick Gammon

Code: [Select]

template<bool Flag> struct foo {static const bool bar = Flag;};
int main() {
    int a;
    int baz = foo<__builtin_constant_p(a)>::bar;
}


Under IDE 1.0.1:

Code: [Select]

sketch_oct01b.cpp:7: sorry, unimplemented: call_expr cannot be mangled due to a defect in the C++ ABI


At least they apologised!

Nick Gammon

I think you are misusing Flag in your first line. Can you explain what the intention is?

pYro_65

Not possible sorry, join my band wagon and encourage Arduino to accept c++11, the constexpr keyword is what you are looking for. I'm pretty sure its just a command line flag change.

C++, as  a rule instantiates everything at compile time ( its how it can optimise ), templates however cannot be instantiated until parameters are provided. So each specific variation of a template is specific to its parameters. Which means you cannot instantiate a  template at runtime as there is no specific parameters compiled for it.

Mutable variables are not considered compile time constants. Even when marked as const ( taking the address can allow changing of the data )


WizenedEE


Code: [Select]
    int a;
Local variables are not initialized, unless you provide an initial value.

Code: [Select]
    int baz = foo<__builtin_constant_p(a)>::bar;
Why would you pass an uninitialized variable to any kind of function, constant or not?


To show an minimal example of my problem.


Code: [Select]

template<bool Flag> struct foo {static const bool bar = Flag;};
int main() {
    int a;
    int baz = foo<__builtin_constant_p(a)>::bar;
}


Under IDE 1.0.1:

Code: [Select]

sketch_oct01b.cpp:7: sorry, unimplemented: call_expr cannot be mangled due to a defect in the C++ ABI


At least they apologised!


It works in gcc 4.7.1, which I have. I guess the gcc people can implement things given 4 years (gcc 4.3.2 was released August 27, 2008)


I think you are misusing Flag in your first line. Can you explain what the intention is?


Well the intention is to use a different struct from that one: http://arduino.cc/forum/index.php/topic,124974


Not possible sorry, join my band wagon and encourage Arduino to accept c++11, the constexpr keyword is what you are looking for. I'm pretty sure its just a command line flag change.

C++, as  a rule instantiates everything at compile time ( its how it can optimise ), templates however cannot be instantiated until parameters are provided. So each specific variation of a template is specific to its parameters. Which means you cannot instantiate a  template at runtime as there is no specific parameters compiled for it.

Mutable variables are not considered compile time constants. Even when marked as const ( taking the address can allow changing of the data )


Well I'm no expert in C++11, but when I try to compile the same program with "-std=c++11" or "-std=gnu++11" I get the same errors. Do you mean that __builtin_constant_p() should be declared constexpr? It's a gcc builtin, so it's not a true function.

Also, the same program does compile without optimizations (with __builtin_constant_p(a) returning 0), and this does compile with __builtin_constant_p(a) as 1. So it's not a problem that the return of __builtin_constant_p() is not constant, it's that it's sometimes constant.
Code: [Select]

template<bool Flag> struct foo {static const bool bar = Flag;};
int main() {
    const int a = 4;
    int baz = foo<__builtin_constant_p(a)>::bar;
}


I'm thinking I'll report it as a bug in gcc, since there's no reason why __builtin_constant_p() would ever return a non-constant.



If nobody thinks that it's possible to make that program work, my next question is "How can I make a variable one type if a different one is constant and a different one if it's not?"
ie, get a to be an int if b is constant, and a to be a long if b is not constant.

pYro_65

I'm not sure if you realise, but my class 'BestFitUInt' is a template metaprogram, it runs entirely during compile time. The class evaporates before runtime leaving you with a 'type' used to declare variables.

With constexpr, you wouldn't need to use templates. The compiler could get the code as close to static as possible while allowing t to be implemented dynamically, still would not be as efficient and most probably equal to making a runtime version of BestFitUInt.

pYro_65

#8
Oct 01, 2012, 02:10 am Last Edit: Oct 01, 2012, 02:13 am by pYro_65 Reason: 1
Oh, and no its not a bug. The compiler ( and __builtin_constant_p ) cannot guarantee 'a' is a compile time constant within its current context.

Haven't tried it but,
Code: [Select]
typedef IF< __builtin_constant_p( a ), int, long >::Result NumType;
NumType n_Number = 4;

WizenedEE


Oh, and no its not a bug. The compiler ( and __builtin_constant_p ) cannot guarantee 'a' is a compile time constant within its current context.

Can you explain that a little more? I know the output of __builtin_constant_p is sometimes constant, but it isn't always. I would think all the compiler needs to know to figure out if it's constant is if it has the "const" attribute or is a literal, but does it need more than that?

pYro_65

#10
Oct 01, 2012, 03:11 am Last Edit: Oct 01, 2012, 03:18 am by pYro_65 Reason: 1
Have you ever written lots of functions without compiling. Then in your ( lets say loop() ) function you use a couple of the functions and compile. Everything goes well so you add in the remainder functions and suddenly you get a syntax/compile error. Even though the function was defined with the error previously, because it wasn't referenced it didn't pass through the compiler.

In reference to __builtin_constant_p, it is seeing 'a' before it has seen the remainder of the function. I think this is hard/impossible to get around.

This is just a thought,
If 'foo' was a type based template, you could create a forward declaration of 'foo'. You can now but it would require a forward declaration for every possible value of 't', which might not be such a bad idea for a range of possible 't'.

But I still don't think you will have luck as the compiler is linear, it still can't guarantee 'a' will not get assigned a value on the next line of code. Even though optimisations can pass many times, __builtin_constant_p is evaluated once.

Go Up