Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get current month index in compile time

Tags:

c++

visual-c++

I'm trying to convert month from __DATE__ in format Mmm to number in compile time. I need it for gcc and MS VC 2012.

my attempt:

template <char C0, char C1, char C2>
struct month{};

template<> struct month < 'J', 'a', 'n'> { static const unsigned id = 1; };
template<> struct month < 'S', 'e', 'p'> { static const unsigned id = 9; };

static const char c0 = __DATE__[0];
static const char c1 = __DATE__[1];
static const char c2 = __DATE__[2];

static const unsigned currId = month<c0, c1, c2>::id;      //this gives error
static const unsigned currId2 = month<'S', 'e', 'p'>::id;  //this is fine

gives in MS VC

error C2970: 'month' : template parameter 'C0' : 'c0' : an expression involving objects with internal linkage cannot be used as a non-type argument

but on ideone works fine.

Is there a way to make this work cross platform/compiler?

edit

I need a compile-time constant So answers in similar question doesn't really help. I need to for example subtract two dates (current and some date in code) and give compile time error when difference between those two is large enough.

like image 701
relaxxx Avatar asked Sep 06 '15 14:09

relaxxx


1 Answers

First: are you sure you need this at compile-time? If run-time is acceptable it's easy: http://www.keil.com/support/docs/1102.htm


But moving away from what's sane, let's have some compile-time fun!

You're using templates here but you really don't need to. You can use a massive expression of doom instead:

static const char c0 = __DATE__[0];
static const char c1 = __DATE__[1];
static const char c2 = __DATE__[2];
static const unsigned int month = (
    c0 == 'J' // Jan Jun Jul
        ? (c1 == 'a' ? 1 : (c2 == 'n' ? 6 : 7))
    : c0 == 'F' ? 2
    : c0 == 'M' // Mar May
        ? (c2 == 'r' ? 3 : 5)
    : c0 == 'A' // Apr Aug
        ? (c1 == 'p' ? 4 : 8)
    : c0 == 'S' ? 9
    : c0 == 'O' ? 10
    : c0 == 'N' ? 11
    : 12
);

Disclaimer: I just wrote that off the top of my head. It works now, but who knows, maybe I got March wrong.

In fact if you want to get even more fun* we can use arithmetic on some characters:

static const char c0 = __DATE__[0];
static const char c1 = __DATE__[1];
static const char c2 = __DATE__[2];
static const unsigned int month = (
    c0 == 'J' // Jan Jun Jul
        ? (c1 == 'a' ? 1 : (c2 == 'n' ? 6 : 7))
    : c0 == 'M' // Mar May
        ? (3 + (c2 == 'y') * 2)
    : c0 == 'A' // Apr Aug
        ? (4 + (c1 == 'u') * 4)
    : c0 == 'S' ? 9
    : c0 <= 'F' ? (12 - (c0 - 'D') * 5) // Feb, Dec
    : (11 + 'N' - c0) // Oct, Nov
);

*: by "fun" I mean: hated by other developers

Since these are const, you can then use it with templates. For example, suppose we've got a contract job which ends in November, and we want to be sure we'll get brought back in for a few days at a high rate once it's over:

#include <iostream>
using namespace std;

static const unsigned int month = ...;

template <int n> class mm {
public:
    static int v;
};

template<> int mm<9>::v=3; // still employed
template<> int mm<10>::v=2; // let's not be too suspicious
template<> int mm<11>::v=1; // patience...
// no value for December - boom! we're in the money! Just in time for Christmas!

int main() {
    std::cout << mm<month>::v;
    return 0;
}

Finally, if you don't want to be littering the global scope, you should use a constexpr function:

static constexpr int getMonth( void ) {
    const char c0 = __DATE__[0];
    const char c1 = __DATE__[1];
    const char c2 = __DATE__[2];
    return (
        c0 == 'J' // Jan Jun Jul
            ? (c1 == 'a' ? 1 : (c2 == 'n' ? 6 : 7))
        : c0 == 'F' ? 2
        : c0 == 'M' // Mar May
            ? (c2 == 'r' ? 3 : 5)
        : c0 == 'A' // Apr Aug
            ? (c1 == 'p' ? 4 : 8)
        : c0 == 'S' ? 9
        : c0 == 'O' ? 10
        : c0 == 'N' ? 11
        : 12
    );
}

...

std::cout << mm<getMonth()>::v;
like image 89
Dave Avatar answered Sep 18 '22 21:09

Dave