Is there a way in c++ to get a different overload called based on the runtime/compile time constness of an input? My version(12) of MSVC can't do this using constexpr. Reading c++ documentation, I am not sure if this is the way constexpr works.
inline int Flip4(constexpr int n) {
return ((n & 0xFF) << 24) | ((n & 0xFF00) << 8) | ((n & 0xFF0000) >> 8) | ((n & 0xFF000000) >> 24);
}
inline int Flip4(int n) {
return _byteswap_ulong(n);
}
int main(int argc, char* argv[]) {
int a = Flip4('abcd'); // calc at compile time
int b = Flip4(argc); // calc at runtime
}
So if this can be done, how? I think there might be a way to use template deduction to do it, but I can't figure out how.
EDIT
I came up with this, but am not sure why it works, && is still fuzy for me, and not sure if this works for everything.
template<class T> typename std::enable_if<std::is_arithmetic<T>::value, int>::type
inline Flip4(T&& n) {
//cout << "compile time" << endl;
return ((n & 0xFF) << 24) | ((n & 0xFF00) << 8) | ((n & 0xFF0000) >> 8) | ((n & 0xFF000000) >> 24);
}
template<class T> typename std::enable_if<!std::is_arithmetic<T>::value, int>::type
inline Flip4(T&& n) {
//cout << "run time" << endl;
return _byteswap_ulong(n);
}
int main(int argc, char* argv[]) {
int n = Flip4(argc);
n += Flip4(1);
return n;
}
If you compile without commenting out the output, it produces this output.
run time
compile time
and it produces this assembly, which is what I want:
int n = Flip4(argc);
000000013FA11270 bswap ecx
n += Flip4(1);
000000013FA11272 lea eax,[rcx+1000000h]
Are there cases of integer T where this won't work?
constexpr
can only be applied to variables and functions, but not function parameters (details on cppreference). Furthermore, you cannot overload the function on whether it is computed at compile or run-time, i.e. this is not valid:
constexpr int Flip4(int n) {
return ((n & 0xFF) << 24) | ((n & 0xFF00) << 8) | ((n & 0xFF0000) >> 8) | ((n & 0xFF000000) >> 24);
}
inline int Flip4(int n) {
return _byteswap_uint64(n);
}
One way is to give the functions different names and call them accordingly.
Just as a side note,
A constexpr
specifier used in a function declaration implies inline
.
So you don't need to declare your constexpr
function inline
Also, don't forget that constexpr
functions are only guaranteed to be evaluated at compile-time if they are used in a context required at compile time. So to force it you would need to write:
constexpr int a = Flip4('abcd');
Your code is illegal.
[C++11, dcl.constexpr] The constexpr
specifier shall be applied only
to the definition of a variable, the declaration of a function or
function template, or the declaration of a static data member of a
literal type (3.9). ... [ Note: Function parameters cannot be
declared constexpr. — end note ]
constexpr
doesn't even exist in MSVC 2013, so you couldn't try it even if you wanted to. Also, if you're wondering why the feature isn't allowed, see constexpr overloading.
To elaborate on my comment, you can try this to work around the limitation you're facing:
Run It Online
#include <iostream>
using std::cout;
using std::endl;
template <int n>
constexpr int Flip4() {
return ((n & 0xFF) << 24) | ((n & 0xFF00) << 8) | ((n & 0xFF0000) >> 8) | ((n & 0xFF000000) >> 24);
}
inline int Flip4(int n) {
return _byteswap_ulong(n);
}
int main() {
constexpr int a = Flip4<0xabcd>(); // calc at compile time
int b = Flip4(0xabcd); // calc at runtime
static_assert(a == -844431360, "");
cout << "a: " << a << endl;
cout << "b: " << b << endl;
}
EDIT: Don't lose hope! User-defined literals are here to the rescue :)
Run It Online
#include <iostream>
using std::cout;
using std::endl;
// wraps a single integer (unsigned long long) in order to use it in a user-defined literal
// the type (unsigned long long) is a limitation of the standard: https://stackoverflow.com/a/16596909/865719
struct IntegerWrapper
{
const unsigned long long value;
constexpr explicit IntegerWrapper(unsigned long long val) : value{val} {}
};
// user-defined literal
constexpr IntegerWrapper operator "" _iw (const unsigned long long value)
{
return IntegerWrapper{value};
}
constexpr int Flip4(IntegerWrapper&& n) {
return ((n.value & 0xFF) << 24) | ((n.value & 0xFF00) << 8) | ((n.value & 0xFF0000) >> 8) | ((n.value & 0xFF000000) >> 24);
}
inline int Flip4(int n) {
return _byteswap_ulong(n);
}
int main() {
constexpr int a = Flip4(0xabcd_iw); // calc at compile time
const int b = Flip4(0xabcd); // calc at runtime
static_assert(a == -844431360, "");
cout << "a: " << a << endl;
cout << "b: " << b << endl;
}