在编译时获取当前月份索引

时间:2015-09-06 14:02:47

标签: c++ visual-c++

我正在尝试将格式为__DATE__的{​​{1}}的月份转换为编译时的数字。我需要它用于gcc和MS VC 2012。

我的尝试:

Mmm

在MS VC中给出

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

但在ideone上工作正常。

有没有办法让这项工作跨平台/编译器?

修改

我需要error C2970: 'month' : template parameter 'C0' : 'c0' : an expression involving objects with internal linkage cannot be used as a non-type argument所以similar question中的答案并没有真正帮助。我需要例如减去两个日期(当前和代码中的某个日期),并在这两个日期之间的差异足够大时给出编译时错误。

3 个答案:

答案 0 :(得分:5)

首先:您确定在编译时需要这个吗?如果运行时间可以接受,那么很简单:http://www.keil.com/support/docs/1102.htm

但是,摆脱目前的理智,让我们有一些编译时的乐趣!

您在这里使用模板,但您真的不需要。你可以使用大量的厄运来表达:

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
);

免责声明:我刚刚写下了我的头脑。它现在有效,但谁知道,也许我错了三月。

事实上,如果你想获得更多乐趣*我们可以对某些角色使用算术:

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
);

*:&#34; fun&#34;我的意思是:被其他开发者讨厌

由于这些是const,因此您可以将它与模板一起使用。例如,假设我们已经签订了一份合同工作,该工作将于11月结束,我们希望确保一旦结束,我们将以高利率将其带回几天:<\ n / p>

#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;
}

最后,如果你不想乱丢全球范围,你应该使用constexpr功能:

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;

答案 1 :(得分:3)

在这里玩得开心......

我的回答需要C ++ 14和一些外部库,但证明了C ++ 14中提供了相当惊人的编译时计算。

首先,我需要在Scott Schurr's str_const处提出C++ Now 2012。这个类是一个编译时字符串,并在this answer中进行了讨论。

然后我需要这个能够进行编译时日期和时间计算的date/time library

接下来,我需要constexpr实施std::find

template <class InputIterator, class T>
constexpr
inline
InputIterator
find(InputIterator first, InputIterator last, const T& value)
{
    for (; first != last; ++first)
        if (*first == value)
            break;
    return first;
}

我可以用str_to_monthstr_const并将其变成date::month

constexpr
date::month
str_to_month(const str_const& m)
{
    constexpr
    str_const months[]
    {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    };
    auto i = ::find(std::begin(months), std::end(months), m);
    if (i == std::end(months))
        throw std::range_error("str_to_month received out of range argument " +
                               std::string(m));
    return date::month{static_cast<unsigned>(i - std::begin(months)) + 1};
}

接下来,我需要一个实用程序将str_const转换为int

constexpr
int
str_to_int(const str_const& s)
{
    int r = 0;
    auto i = s.begin();
    for (; i != s.end() && *i == ' '; ++i)
        ;
    for (; i != s.end(); ++i)
    {
        r *= 10;
        r += *i - '0';
    }
    return r;
}

(最小错误检查)

最后,我可以使用这些实用程序将str_const转换为date::year_month_day

// Assume the form used by __DATE__: Mmm dd yyyy
constexpr
date::year_month_day
str_to_year_month_day(const str_const& s)
{
    return str_to_month(s.substr(0, 3))
          /str_to_int(s.substr(4, 2))
          /str_to_int(s.substr(7));
}

我刚刚使用以下main对所有这些进行了操作,constexpr使用static_assert计算所有内容,并使用int main() { constexpr auto ymd = str_to_year_month_day(__DATE__); using namespace date; static_assert(ymd == sep/6/2015, ""); constexpr auto ymwd = year_month_weekday{ymd}; static_assert(ymwd == sun[1]/sep/2015, ""); } 确认计算:

constexpr

我在2015年9月6日编制了这个程序,这恰好是本月的第一个星期日。

你需要gcc或clang才能做到这一点。即使是最新的VS-2015也没有达到aabbc.txt aaabbxx.txt aacbbbv.txt 的规格,足以在编译时进行这些计算。

答案 2 :(得分:0)

C ++ 14允许您使用适当的switch-case语句来执行此操作,该语句比滥用三元条件运算符更容易使用:

static constexpr uint8_t get_month(const char date[])
{
    switch (date[0])
    {
        case 'J':
            if      (date[1] == 'a') return 1;  //Jan
            else if (date[2] == 'n') return 6;  //Jun
            else                     return 7;  //Jul
        case 'M':
            if (date[2] == 'r') return 3;  //Mar
            else                return 5;  //May
        case 'A':
            if (date[1] == 'p') return 4;  //Apr
            else                return 8;  //Aug
        case 'F': return 2;    //Feb
        case 'S': return 9;    //Sep
        case 'O': return 10;   //Oct
        case 'N': return 11;   //Nov
        case 'D': return 12;   //Dec
        default:  return 0;    //Error
    }

您可以像get_month(__DATE__)那样调用它,也可以摆脱参数而直接使用__DATE__