constexpr function not calculate value in compile time - c++

I want to compare meta programming and use of constexpr in c++0x.
then I write a fib function in both model.
when I use meta programming model, answer print out very fast because it calculated in compile time. but when I use constexpr funcion it calculate value in run time, not in compile time.
I using g++( gcc ) 4.8 .can any body help me?
#include <iostream>
using namespace std;
#define NUM 42
template <unsigned int N>
struct Fibonacci {
enum { value = Fibonacci<N - 1>::value + Fibonacci<N - 2>::value };
};
template <>
struct Fibonacci<1> {
enum { value = 1 };
};
template <>
struct Fibonacci<0> {
enum { value = 1 };
};
constexpr unsigned int fib(unsigned int n)
{
return (n > 1 ? fib(n-1) + fib(n-2) : 1 );
}
int main()
{
cout << "Meta_fib(NUM) : " << Fibonacci<NUM>::value << endl; // compile time :)
cout << "Constexpr_fib(NUM) : " << fib(NUM) << endl; // run time :-?
return 0;
}

I believe the reason is that constexpr is not guaranteed to execute at compile-time. To enforce compile-time evaluation, you have to assign it to a compile-time alias. Like,
enum {i = fib(NUM)};

With gcc, at least, you can get the constexpr value to be computed at compile time by making it a static variable:
static const unsigned fibNUM = fib(NUM);
As I read the standard, it's still allowed to compute the value at startup, but in practice it will be computed at compile time.

A simple test to see if your constexpr are really being done at compile-time is to use an std::array:
#include <array>
std::array<int, Fibonacci<5>::value> arr;
std::array<int, fib(5)> arr2;
gcc has no complaints.
See this comment by Bjarne Stroustrup:
... according to the standard a constexpr function may be evaluated at
compiler time or run time unless it is used as a constant expression,
in which case it must be evaluated at compile-time. To guarantee
compile-time evaluation, we must either use it where a constant
expression is required (e.g., as an array bound or as a case label) or
use it to initialize a constexpr. I would hope that no self-respecting
compiler would miss the optimization opportunity to do what I
originally said: "A constexpr function is evaluated at compile time if
all its arguments are constant expressions."

constexpr is not guaranteed to be evaluated at compile time. This means, compiler can choose whether to evaluate at compile time or at run time.
You can try to assign it to a compile time constant and check like this...
const long i = fib(NUM);// here i should be initialized at the time of
// declaration
cout << "Meta_fib(NUM) : " << Fibonacci<NUM>::value << endl;
cout << "Constexpr_fib(NUM) : " << i << endl;

Related

How to check at compile time that a function may be called at compile time? [duplicate]

Since the extended versions of constexpr (I think from C++14) you can declare constexpr functions that could be used as "real" constexpr. That is, the code is executed at compile time or can behave as inline functions. So when can have this program:
#include <iostream>
constexpr int foo(const int s) {
return s + 4;
}
int main()
{
std::cout << foo(3) << std::endl;
const int bar = 3;
std::cout << foo(bar) << std::endl;
constexpr int a = 3;
std::cout << foo(a) << std::endl;
return 0;
}
The result is:
7
7
7
So far so good.
Is there a way (possibly standard) to know inside foo(const int s) if the function is executed at compile time or at runtime?
EDIT: Also is it possible to know at runtime if a function was evaluated at compile time?
C++20 introduces is_constant_evaluated, defined in header <type_traits>, which addresses this issue.
constexpr int foo(int s)
{
if (std::is_constant_evaluated()) // note: not "if constexpr"
/* evaluated at compile time */;
else
/* evaluated at run time */;
}
Note that here the ordinary if is used instead of if constexpr. If you use if constexpr, then the condition has to be evaluated at compile time, so is_constant_evaluated always returns true, rendering the test useless.
The technique listed works, but since it uses static_assert it is not sfinae friendly. A better way (in theory, you'll see what I mean) to do this is to check whether a function is noexcept. Why? Because, constant expressions are always noexcept, even if the functions are not marked as such. So, consider the following code:
template <class T>
constexpr void test_helper(T&&) {}
#define IS_CONSTEXPR(...) noexcept(test_helper(__VA_ARGS__))
test_helper is constexpr, so it will be a constant expression as long as its argument is. If it's a constant expression, it will be noexcept, but otherwise it won't be (since it isn't marked as such).
So now let's define this:
double bar(double x) { return x; }
constexpr double foo(double x, bool b) {
if (b) return x;
else return bar(x);
}
foo is only noexcept if the x is a constant expression, and b is true; if the boolean is false then we call a non constexpr function, ruining our constexpr-ness. So, let's test this:
double d = 0.0;
constexpr auto x = IS_CONSTEXPR(foo(3.0, true));
constexpr auto y = IS_CONSTEXPR(foo(3.0, false));
constexpr auto z = IS_CONSTEXPR(foo(d, true));
std::cerr << x << y << z;
It compiles, great! This gives us compile time booleans (not compile failures), which can be used for sfinae, for example.
The catch? Well, clang has a multi-year bug, and doesn't handle this correctly. gcc however, does. Live example: http://coliru.stacked-crooked.com/a/e7b037932c358149. It prints "100", as it should.
I think the canonical way to do that is with static_assert. static_asserts are evaluated at compile time, so they will break the build if their condition is false.
#include <iostream>
constexpr int foo(const int s) {
return s + 4;
}
int main()
{
std::cout << foo(3) << std::endl;
const int bar = 3;
std::cout << foo(bar) << std::endl;
constexpr int a = 3;
std::cout << foo(a) << std::endl;
static_assert(foo(3) == 7, "Literal failed");
static_assert(foo(bar) == 7, "const int failed");
static_assert(foo(a) == 7, "constexpr int failed");
return 0;
}
clang++ -std=c++14 so1.cpp compiles fine for me, showing that everything works as expected.
If you can use C++20, there is std::is_constant_evaluated which does exactly what you want. std::is_constant_evaluated is typically implemented using a compiler intrinsic.
This is called __builtin_is_constant_evaluated in GCC and clang, so you can implement your own "safe" wrapper around it, even in C++17 and lower.
// if C++20, we will need a <type_traits> include for std::is_constant_evaluated
#if __cplusplus >= 202002L
#include <type_traits>
#endif
constexpr bool is_constant_evaluated() {
#if __cplusplus >= 202002L
return std::is_constant_evaluated();
#elif defined(__GNUC__) // defined for both GCC and clang
return __builtin_is_constant_evaluated();
#else
// If the builtin is not available, return a pessimistic result.
// This way callers will implement everything in a constexpr way.
return true;
#endif
}
Note that this builtin is still relatively new (GCC 9.0+) so you might also want to detect the compiler version.
Within a constexpr function, you couldn't tell if you are being evaluated in a constexpr context prior to c++20. Since c++20, this functionalty was added -- constexpr bool std::is_constant_evaluated() will tell you if you are being called in a constexpr context.
Outside a constexpr function, there are a number of ways to determine if a call to a function with a certain set of arguments would be evaluated in a constexpr context. The easiest would be to use the result in a context requiring constexpr.
Assuming your constexpr expression returns a non-void integral or pointer type (including function pointer):
#define CONSTEXPR_EVAL(...) \
std::integral_constant< \
std::decay_t<decltype(__VA_ARGS__)>, \
__VA_ARGS__ \
>::value
then CONSTEXPR_EVAL( bar(foo, true) ) will fail to compile if bar(foo, true) cannot be evaluated at compile time, and if it can be evaluated at compile time it returns that value.
Other tricks involving noexcept (a function evaluated at compile time is noexcept) can work (see #NirFriedman's answer).
Based on the information in this discussion, I crafted the following minimal example:
template <class T>
constexpr void test_helper(T &&) {}
#define IS_CONSTEXPR(...) noexcept(test_helper(__VA_ARGS__))
constexpr void test(){
static_assert(IS_CONSTEXPR(10), "asdfadsf");
constexpr const int x = 10;
static_assert(IS_CONSTEXPR(x), "asdfadsf");
int y;
static_assert(IS_CONSTEXPR(y), "asdfadsf");
}
int main(){
test();
return 0;
}
To my disappointment, It it fails to compile at each of the static_asserts. see https://www.godbolt.org/z/Tr3z93M3s
Sorry for spoiling the party, but there is certainly no standard way of doing this. Under the as-if rule, the compiler could emit code that calculates the result at run time even in such cases where it has already been forced to calculate it at compile time in a different context. Anything that can be done at compile time can be done again at run time, right? And the calculation has already been proven not to throw.
By extension, then, any standard-compliant IS_REALLY_CONSTEXPR or is_really_constexpr check cannot disprove that the exact same call, or for that matter, the value of the exact same constexpr symbol involves a run time calculation.
Of course there usually isn't any reason to repeat a calculation at run time that can be done or even has already been done at compile time, but the question was about telling whether the compiler uses the precalculated result, and there isn't one.
Now you did say possibly standard, so in effect your best bet is probably to test one of the provided solutions with your compiler of choice and hope that it behaves consistently. (Or reading the source code if it is open/public source and you are so inclined.)

Is it possible to test if a constexpr function is evaluated at compile time?

Since the extended versions of constexpr (I think from C++14) you can declare constexpr functions that could be used as "real" constexpr. That is, the code is executed at compile time or can behave as inline functions. So when can have this program:
#include <iostream>
constexpr int foo(const int s) {
return s + 4;
}
int main()
{
std::cout << foo(3) << std::endl;
const int bar = 3;
std::cout << foo(bar) << std::endl;
constexpr int a = 3;
std::cout << foo(a) << std::endl;
return 0;
}
The result is:
7
7
7
So far so good.
Is there a way (possibly standard) to know inside foo(const int s) if the function is executed at compile time or at runtime?
EDIT: Also is it possible to know at runtime if a function was evaluated at compile time?
C++20 introduces is_constant_evaluated, defined in header <type_traits>, which addresses this issue.
constexpr int foo(int s)
{
if (std::is_constant_evaluated()) // note: not "if constexpr"
/* evaluated at compile time */;
else
/* evaluated at run time */;
}
Note that here the ordinary if is used instead of if constexpr. If you use if constexpr, then the condition has to be evaluated at compile time, so is_constant_evaluated always returns true, rendering the test useless.
The technique listed works, but since it uses static_assert it is not sfinae friendly. A better way (in theory, you'll see what I mean) to do this is to check whether a function is noexcept. Why? Because, constant expressions are always noexcept, even if the functions are not marked as such. So, consider the following code:
template <class T>
constexpr void test_helper(T&&) {}
#define IS_CONSTEXPR(...) noexcept(test_helper(__VA_ARGS__))
test_helper is constexpr, so it will be a constant expression as long as its argument is. If it's a constant expression, it will be noexcept, but otherwise it won't be (since it isn't marked as such).
So now let's define this:
double bar(double x) { return x; }
constexpr double foo(double x, bool b) {
if (b) return x;
else return bar(x);
}
foo is only noexcept if the x is a constant expression, and b is true; if the boolean is false then we call a non constexpr function, ruining our constexpr-ness. So, let's test this:
double d = 0.0;
constexpr auto x = IS_CONSTEXPR(foo(3.0, true));
constexpr auto y = IS_CONSTEXPR(foo(3.0, false));
constexpr auto z = IS_CONSTEXPR(foo(d, true));
std::cerr << x << y << z;
It compiles, great! This gives us compile time booleans (not compile failures), which can be used for sfinae, for example.
The catch? Well, clang has a multi-year bug, and doesn't handle this correctly. gcc however, does. Live example: http://coliru.stacked-crooked.com/a/e7b037932c358149. It prints "100", as it should.
I think the canonical way to do that is with static_assert. static_asserts are evaluated at compile time, so they will break the build if their condition is false.
#include <iostream>
constexpr int foo(const int s) {
return s + 4;
}
int main()
{
std::cout << foo(3) << std::endl;
const int bar = 3;
std::cout << foo(bar) << std::endl;
constexpr int a = 3;
std::cout << foo(a) << std::endl;
static_assert(foo(3) == 7, "Literal failed");
static_assert(foo(bar) == 7, "const int failed");
static_assert(foo(a) == 7, "constexpr int failed");
return 0;
}
clang++ -std=c++14 so1.cpp compiles fine for me, showing that everything works as expected.
If you can use C++20, there is std::is_constant_evaluated which does exactly what you want. std::is_constant_evaluated is typically implemented using a compiler intrinsic.
This is called __builtin_is_constant_evaluated in GCC and clang, so you can implement your own "safe" wrapper around it, even in C++17 and lower.
// if C++20, we will need a <type_traits> include for std::is_constant_evaluated
#if __cplusplus >= 202002L
#include <type_traits>
#endif
constexpr bool is_constant_evaluated() {
#if __cplusplus >= 202002L
return std::is_constant_evaluated();
#elif defined(__GNUC__) // defined for both GCC and clang
return __builtin_is_constant_evaluated();
#else
// If the builtin is not available, return a pessimistic result.
// This way callers will implement everything in a constexpr way.
return true;
#endif
}
Note that this builtin is still relatively new (GCC 9.0+) so you might also want to detect the compiler version.
Within a constexpr function, you couldn't tell if you are being evaluated in a constexpr context prior to c++20. Since c++20, this functionalty was added -- constexpr bool std::is_constant_evaluated() will tell you if you are being called in a constexpr context.
Outside a constexpr function, there are a number of ways to determine if a call to a function with a certain set of arguments would be evaluated in a constexpr context. The easiest would be to use the result in a context requiring constexpr.
Assuming your constexpr expression returns a non-void integral or pointer type (including function pointer):
#define CONSTEXPR_EVAL(...) \
std::integral_constant< \
std::decay_t<decltype(__VA_ARGS__)>, \
__VA_ARGS__ \
>::value
then CONSTEXPR_EVAL( bar(foo, true) ) will fail to compile if bar(foo, true) cannot be evaluated at compile time, and if it can be evaluated at compile time it returns that value.
Other tricks involving noexcept (a function evaluated at compile time is noexcept) can work (see #NirFriedman's answer).
Based on the information in this discussion, I crafted the following minimal example:
template <class T>
constexpr void test_helper(T &&) {}
#define IS_CONSTEXPR(...) noexcept(test_helper(__VA_ARGS__))
constexpr void test(){
static_assert(IS_CONSTEXPR(10), "asdfadsf");
constexpr const int x = 10;
static_assert(IS_CONSTEXPR(x), "asdfadsf");
int y;
static_assert(IS_CONSTEXPR(y), "asdfadsf");
}
int main(){
test();
return 0;
}
To my disappointment, It it fails to compile at each of the static_asserts. see https://www.godbolt.org/z/Tr3z93M3s
Sorry for spoiling the party, but there is certainly no standard way of doing this. Under the as-if rule, the compiler could emit code that calculates the result at run time even in such cases where it has already been forced to calculate it at compile time in a different context. Anything that can be done at compile time can be done again at run time, right? And the calculation has already been proven not to throw.
By extension, then, any standard-compliant IS_REALLY_CONSTEXPR or is_really_constexpr check cannot disprove that the exact same call, or for that matter, the value of the exact same constexpr symbol involves a run time calculation.
Of course there usually isn't any reason to repeat a calculation at run time that can be done or even has already been done at compile time, but the question was about telling whether the compiler uses the precalculated result, and there isn't one.
Now you did say possibly standard, so in effect your best bet is probably to test one of the provided solutions with your compiler of choice and hope that it behaves consistently. (Or reading the source code if it is open/public source and you are so inclined.)

Can C++ compilers cache the result of constexpr functions?

Disclaimer: This question is a bit complicated because it's several questions in one, but they're all related to the same sort of concept/issue.
Premise:
consexpr functions may only consist of a single return statement.
They can call other functions and use conditionals, but theoretically they should demonstrate functional purity, and thus the results should be cachable (by the compiler, at compile time) in some sort of map so that the compiler doesn't have to constantly re-evaluate the same function.
Question(s):
Is this assumption correct or is there something I haven't considered that makes it impossible to cache the result of a constexpr function?
If no, does this mean that constexpr functions have to be calculated every time they're used?
What about templates? Are constexpr values on templates cachable or do they also have to be recalculated each time?
I don't believe constexpr functions are required to be pure - at least, not for all possible arguments. Consider:
#include <iostream>
#include <stdlib.h>
constexpr int f(int n) { return n == 0 ? 42 : rand(); }
template <int n> void g() {}
int main()
{
g<f(0)>(); // OK
// g<f(1)>(); // error
std::cout << f(0) << ' ' << f(1) << ' ' << f(2);
}
Output: 42 1804289383 846930886. Demo

Constexpr is this correct behaviour?

I'm not sure this is correct behaviour. So I hadn't messed with constexpr before and want to make sure I'm not misunderstanding something about the spec. Testing in the way MSDN mentions to test it. If I put a breakpoint in the function and it gets skipped then it was evaluated at compile time. Is the behaviour below normal?
https://msdn.microsoft.com/en-us/library/dn956974.aspx
It only seems to work with optimization on period.
It only works if I set the value to a constexpr variable or in specific use cases that have NOTHING to do with the input variable.
So for some reason this works
constexpr unsigned int factorial(unsigned int n)
{
return n <= 1 ? 1 : n*factorial(n - 1);
}
constexpr unsigned int value = factorial(5);
std::cout << value << std::endl;
but this gets ran at run time
constexpr unsigned int factorial(unsigned int n)
{
return n <= 1 ? 1 : n*factorial(n - 1);
}
std::cout << factorial(5) << std::endl;
Seems kinda inconvenient to have to make a constexpr variable. It seems to work in some other specialized cases.
switch (fnv1a("Hello"))
{
case fnv1a("GoodBye"):
std::cout << "GoodBye" << std::endl;
break;
case fnv1a("Hello"):
std::cout << "Hello" << std::endl;
break;
default:
break;
}
This case initialized the case values to constants but the call in the switch statement doesn't evaluate to a constexpr and gets ran at run time.
Also equality operators seem to work for some reason so. This example only calls fnv1a once for the dynamic value.
void isValue(const char* str)
{
if (fnv1a(str) == fnv1a("Hello"));
std::cout << "Found it!" << std::endl;
}
I know MSVC has had some issues with compliance, but the behaviour not evaluating to a constant based on the use case rather then if the input is constant just seems odd to me.
The behavior you observe is correct. A constexpr function is required to be called at compile-time on constant expressions only. This includes the cases where the return value of a constexpr function is used as an initializer for constexpr variables, or as the value for a non-type template parameter.
In all other cases, the compiler is not required to evaluate the function at compile-time.

C++ Switch won't compile with externally defined variable used as case

I'm writing C++ using the MinGW GNU compiler and the problem occurs when I try to use an externally defined integer variable as a case in a switch statement. I get the following compiler error: "case label does not reduce to an integer constant".
Because I've defined the integer variable as extern I believe that it should compile, does anyone know what the problem may be?
Below is an example:
test.cpp
#include <iostream>
#include "x_def.h"
int main()
{
std::cout << "Main Entered" << std::endl;
switch(0)
{
case test_int:
std::cout << "Case X" << std::endl;
break;
default:
std::cout << "Case Default" << std::endl;
break;
}
return 0;
}
x_def.h
extern const int test_int;
x_def.cpp
const int test_int = 0;
This code will compile correctly on Visual C++ 2008. Furthermore a Montanan friend of mine checked the ISO C++ standard and it appears that any const-integer expression should work. Is this possibly a compiler bug or have I missed something obvious?
Here's my compiler version information:
Reading specs from C:/MinGW/bin/../lib/gcc/mingw32/3.4.5/specs
Configured with: ../gcc-3.4.5-20060117-3/configure --with-gcc --with-gnu-ld --with-gnu-as --host=mingw32 --target=mingw32 --prefix=/mingw --enable-threads --disable-nls --enable-languages=c,c++,f77,ada,objc,java --disable-win32-registry --disable-shared --enable-sjlj-exceptions --enable-libgcj --disable-java-awt --without-x --enable-java-gc=boehm --disable-libgcj-debug --enable-interpreter --enable-hash-synchronization --enable-libstdcxx-debug
Thread model: win32
gcc version 3.4.5 (mingw-vista special r3)
A case label requires an integral constant expression which have strict requirements that enable their value to be determined at compile time at the point of use.
From 5.19 [expr.const], "an integral constant expression can involve only literals (2.13), enumerators, const variables or static data members of integral or enumeration types initialized with constant expressions (8.5),...".
At the point at which you use test_int where a constant expression is required, it is a const variable declared extern and without any initializer and does not meet the requirements for a constant expression, despite the fact that you do actually initialize it with a integral constant expression in another translation unit. (*This is not completely clear from the wording of the standard but is my current interpretation of it.)
The restrictions in the standard disallow usages such as:
void f(int a, int b)
{
const int c = b;
switch (a)
{
case c:
//...
}
}
In your example, when the compiler is compiling test.cpp, it has no way to determine what the initializer might be in x_def.cpp. You might have done:
const int test_int = (int)time();
Clearly, in neither of these examples could the value of the const int be determined at compile time which is the intention for integral constant expressions.
Case labels have to be compile-time constants. That means the compiler must be able to substitute the value at compile-time. Although your values are constant, the compiler can't know their values until at least link-time.
VC++ is right, and g++ is wrong. A case label is required to be an integral constant expression (§6.4.2/2) and a const variable of integer type initialized with a constant expression is a constant expression (§5.19/1).
Edit:mostly for Pavel, and his suggestion of a possible DR. §5.19/2 has been completely rewritten already. C++0x adds a whole new concept of a constexpr that expands what's considered a constant expression considerably. For example, under the current standard, something like:
int x() { return 10; }
const int y = x();
y is not a constant expression. We can all easily see that it's (indirectly) initialized with a literal 10, but the compiler still can't allow it as a constant expression. Under the new standard, it'll be possible designate x() as a constexpr, and y will be a constant expression.
As it's formulated in N2960, §5.19/2 says an expression is a constant expression unless it uses something from the following list. It then gives about a page-long list, but using a const variable that isn't initialized in the current compilation unit doesn't seem to be one of them. [Edit: see below -- reading CWG Issue 721, I've changed my mind.]
As far as VC++ being right and g++ wrong, I meant only in this very specific respect. There's no question that both are "wrong" if you're talking about getting every part of the standard correct. I doubt anybody's even working on implementing export for either one.
export does, however, point out a degree to which C++ seems willing to postpone decisions until link time. Two-phase name lookup means that when an exported template is compiled, there's a lot more than just constant expressions that it doesn't know for sure. It might not even know whether a particular name refers to a function or an object -- but there's no question that the standard does require exactly that. The issue at hand strikes me as a substantially simpler one to deal with.
Edit: I did a bit of searching, and found Core Working Group Issue 721. Jame's question parallels the one at hand quite closely ("However, this does not require, as it presumably should, that the initialization occur in the same translation unit and precede the constant expression..."). The proposed resolution adds the phrase: "...with a preceding initialization...". At least as I read it, that means that the committee agreed that under the current standard, the code must be accepted, but under the new standard it's not allowed.
That wording was agreed upon in July of this year, but doesn't (yet?) appear in N2960, which I believe is the most recent draft of C++0x.
I cannot reproduce this on a trivial example using VC++2008:
test.cpp:
extern const int n;
int main() {
switch (0) {
case n: break;
}
}
test2.cpp:
extern const int n = 123;
compile with:
cl.exe test.cpp test2.cpp
output:
test.cpp(4) : error C2051: case expression not constant
MS compiler is being a bit naughty here. When you compile the the constant initialization and the case statement using the constant in the same compilation unit it works out the constant value at compile time.
Once you attempt to use the extern const outside of the compilation unit where it's initialised (i.e. the cpp file containing initialization or any of the files it includes) the compiler will barf with pretty much the same error. Fred Larson is correct the compiler shouldn't know the constant value until link time and thus it must not be acceptable as a switch constant, it's just MS compiler cheats a little bit.
The solution to your problem would be to use macros, is there any reason why you don't want to #define the constant?
Here's a simpler test:
test_int.cpp:
const int test_int = 10;
main.cpp:
#include <iostream>
using std::cout;
using std::endl;
extern const int test_int;
int main() {
cout << test_int << endl;
return 0;
}
In G++, I get an undefined reference. However, doing the same thing in C works. According to http://gcc.gnu.org/ml/gcc/2005-06/msg00325.html , a const variable implicitly has internal linkage in C++. This doesn't appear to be the case in C.
I'm using a "gcc (SUSE Linux) 4.3.2" and having a similar effect, that still is a bit stranger.
My definitions are:
namespace operations{
const cOpDummy OpDummy();
const cInitOperator InitOperator();
};
const unsigned long ulNumberOfOperations = 2;
const cOperation * arrayOperations[] = {
& (operations::OpDummy),
& (operations::InitOperator)
};
And the extern declarations in an other file are:
extern const unsigned long ulNumberOfOperations;
extern const cOperation * arrayOperations[];
The funny thing is: The compiler gives just for "ulNumberOfOperations" "undefined reference to ulNumberOfOperations", but is Ok with "arrayOperations[]".
My workaround is to declare "ulNumberOfOperations" not constant.
Since c++11 you could build a little template framework to give you a syntax like this:
void test(int a, int x, int y, int z)
{
std::cout << "given " << a << ", choosing ";
given(a)
.when(x, [] { std::cout << "x\n"; })
.when(y, [] { std::cout << "y\n"; })
.when(z, [] { std::cout << "z\n"; })
.when(any_other, [] { std::cout << "none of the above\n"; });
}
Full demo:
#include <iostream>
struct any_object {};
constexpr auto any_other = any_object {};
template<class Expr>
struct when_object
{
template<class T, class F>
constexpr when_object& when(T const& value, F&& f)
{
if (not executed and expr == value) {
executed = true;
f();
}
return *this;
}
template<class F>
constexpr void when(any_object, F&& f)
{
if (not executed) {
executed = true;
f();
}
}
Expr const& expr;
bool executed = false;
};
template<class Expr>
constexpr auto given(Expr const& expr)
{
return when_object<Expr> {expr};
}
void test(int a, int x, int y, int z)
{
std::cout << "given " << a << ", choosing ";
given(a)
.when(x, [] { std::cout << "x\n"; })
.when(y, [] { std::cout << "y\n"; })
.when(z, [] { std::cout << "z\n"; })
.when(any_other, [] { std::cout << "none of the above\n"; });
}
int main()
{
test(4, 4, 5, 6);
test(4, 3, 4, 5);
test(4, 2, 3, 4);
test(1, 2, 3, 4);
}
expected results:
given 4, choosing x
given 4, choosing y
given 4, choosing z
given 1, choosing none of the above