2

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?

johnnycrash
  • 5,184
  • 5
  • 34
  • 58
  • No, there are no `constexpr` parameters. – Bo Persson Oct 17 '15 at 23:44
  • How can i do something like this. – johnnycrash Oct 17 '15 at 23:46
  • How about changing the first "overload" to somthing like `template inline int Flip4() { ... }` and then calling it with `Flip4<0xc0de>()` ? – maddouri Oct 17 '15 at 23:50
  • Are you trying to use an intrinsic or assembly at compile time? – Jason Oct 17 '15 at 23:54
  • @865719. That wont work for Flip4(). I'd like to make a library function that can make use of template metaprogramming if the argument is a constant, or call a slow runtime equivalent if not. – johnnycrash Oct 18 '15 at 00:01
  • @Jason Say I have legacy code that calls Flip4('1234') and Flip4(n). Right now I use the first Flip4 func since a compile time const is the most common case. Unfortunately if the input is a runtime variable, then that code is not optimal. Instead I want to use the intrinsic. The issue is creating a single function that overloads based on n being a compile time constant or not. – johnnycrash Oct 18 '15 at 00:03
  • Clang will compile the above into the single `bswap` instruction (even a generalized one). If you can use clang you could just define it as a `constexpr` function, but I'm not sure that helps in this scenario. – Jason Oct 18 '15 at 00:06
  • @Jason So will gcc actually, according to [this](http://gcc.godbolt.org). – Rostislav Oct 18 '15 at 00:13
  • I don't want to use bswap if the value is a runtime const, as that would be slower than just calculating FLIP the slow way and letting the compiler work it out at compile time. – johnnycrash Oct 18 '15 at 00:15
  • @johnnycrash If you leave it at your first version (the one with shifts, but without constexpr), both gcc and clang will generate one bswap for runtime and nothing for compile time - just the actual resulting value. Not sure what will it do with `_byteswap_ulong`. Check your compiler assembly output. – Rostislav Oct 18 '15 at 00:26
  • 1
    Just checked - VS2015 computes the value for constant input also during compile time when you use `_byteswap_ulong`. So all is good - no actual need for `constexpr` at all. The assembly for the code in the link in my previous comment (but using `_byteswap_ulong` instead of shifts) is `bswap ecx` and `lea eax,[rcx+64636261h]`. – Rostislav Oct 18 '15 at 00:35
  • 1
    Clang and recent versions of GCC should compute the values at compile time and emit `bswap` for non-const values with `Flip4` only defined as `constexpr`. According to @Rostislav, MSVC '15 can evaluate the constants with the intrinsic at compile time as well. – Jason Oct 18 '15 at 00:40
  • @Rostislav Ahhh well knowing vs2015 does that is nice! – johnnycrash Oct 18 '15 at 00:44
  • I added a template version of Flip4 which seems to work, but I'm sure it has issues. I am going to not worry about it for _byteswap_xxx, but I still need a working technique like this for other similar situations. I am not sure if my solution is good though. – johnnycrash Oct 18 '15 at 00:51
  • I am feeling stupid. I looked at the disassembly for _byteswap_ulong(10) and vs2013 correctly computes the value at runtime! I still would like to figure out how to do this with overloading though! – johnnycrash Oct 18 '15 at 00:54
  • Oh i remember now what happened. I was using Flip4 in a case statement and it doesn't like the intrinsic. When I get to vs2015 I should be able to put constexpr in front of my second templated version and have it succeed there, right? – johnnycrash Oct 18 '15 at 00:57
  • From what @Rostislav described, you shouldn't even need the `constexpr` with VS '15. I do know Clang will eagerly evaluate functions at compile time without `constexpr` as part of constant folding, but I can't personally verify anything else. – Jason Oct 18 '15 at 01:05
  • Would constexpr allow you to use Flip4(n) in a case statement? "case Flip4(42):" – johnnycrash Oct 18 '15 at 23:05
  • @johnnycrash I don't understand how your compile-time function can print out "compile time"? std::cout is run-time. How can compile-time function use cout? Are you sure it is really compile-time? – zupazt3 Jun 09 '17 at 12:16

3 Answers3

2

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');
Rostislav
  • 3,857
  • 18
  • 30
  • You could use a defaulted argument `bool compile_time = true`, and then call `Flip4(stuff, false)` to invoke the runtime version explicitly. – melak47 Oct 17 '15 at 23:50
  • @melak47 Yeah - there are many different ways to achieve this. Changed the wording :) Thanks! – Rostislav Oct 17 '15 at 23:53
  • Thanks for the implied inline note. I didn't know that! – johnnycrash Oct 18 '15 at 00:22
  • I have to reasons for needing overload resolution. 1) How shall we say it... unsophisticated developers, and 2) legacy code. – johnnycrash Oct 18 '15 at 00:23
  • @johnnycrash Yeah, got it from your other comments. See my last comment to the original post - perhaps it will help. – Rostislav Oct 18 '15 at 00:27
1

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.

  • Note that in the linked answer, I provided an argument against the particular intended use (string pooling) of `constexpr`-overloading, and not against the hypothetical feature in general. – Ben Voigt Oct 17 '15 at 23:59
  • Its a problem for me since I can only speed up runtime constant or non runtime constant cases, but not both - unless I create 2 different functions. – johnnycrash Oct 18 '15 at 00:20
1

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;
}
maddouri
  • 3,737
  • 5
  • 29
  • 51