#include <iostream>

/* strlen as constant expression */
constexpr int my_strlen(const char *s)
{
    const char *p = s;
    while ('\0' != *p) ++p;
    return p-s;
}

/* Degree to radian conversion as constant expression */
constexpr const float pi = 3.14f; // const is not enough!

constexpr float degree2radian(float degree)
{
  return degree * pi / 180.0f;
}

/* Power function as constant expression */
// C++11
constexpr int my_pow(int base, int exp)
{
    return  exp == 0 ? 1 : base * my_pow(base, exp-1);
}

// C++14
/*constexpr int my_pow(int base, int exp)
{
  auto result = 1;
  for (int i = 0; i < exp; ++i)
    result *= base;
  return result;
}*/

// Output function that requires a compile-time constant, for testing
template<int n> struct constN
{
    constN() { std::cout << n << std::endl; }
};

int main()
{
  // C++11:
  // prog.cpp: In function 'constexpr int my_strlen(const char*)':
  // prog.cpp:9:1: error: body of constexpr function 'constexpr int my_strlen(const char*)' not a return-statement
  std::cout << "String length: " << my_strlen("Hello World!") << std::endl;
  
  std::cout << degree2radian(60.0f) << std::endl;

  int b = 4;                                                // could be user input
  std::cout <<     "2^10 = "; constN<my_pow(2, 10)> out1;   // guaranteed to be evaluated in compile time
  std::cout <<     "3^10 = " << my_pow(3, 10) << std::endl; // can be evaluated in compile time
  std::cout << b << "^10 = " << my_pow(b, 10) << std::endl; // computed at runtime

  // prog.cpp:32:51: error: the value of 'b' is not usable in a constant expression
  // test.cpp:25:7: note: 'int b' is not const
  std::cout << b << "^10 = "; constN<my_pow(b, 10)> out2; 
  return 0;
}
