在编译时获取当前月份索引
Get current month index in compile time
我正在尝试将 __DATE__
格式的月份 Mmm
转换为编译时的数字。我需要 gcc 和 MS VC 2012.
我的尝试:
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
在 MS VC
error C2970: 'month' : template parameter 'C0' : 'c0' : an expression involving objects with internal linkage cannot be used as a non-type argument
但在 ideone 上工作正常。
有没有办法让这个作品跨platform/compiler?
编辑
我需要 compile-time constant
所以 similar question 中的答案并没有多大帮助。例如,我需要减去两个日期(代码中的当前日期和某个日期),并在这两个日期之间的差异足够大时给出编译时错误。
首先:你确定你在编译时需要这个吗?如果 运行-time 是可以接受的,那很简单: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
);
免责声明:我只是在脑海中写下了这一点。现在可以用了,但谁知道呢,也许我弄错了 March。
事实上,如果您想获得更多乐趣*,我们可以对某些字符使用算术:
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" 我的意思是:被其他开发者讨厌
由于这些是常量,因此您可以将其与模板一起使用。例如,假设我们有一份合同工作将于 11 月结束,我们希望确保在合同结束后我们能以高利率被带回工作几天:
#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;
在这里玩得开心...
我的回答需要 C++14 和一些外部库,但证明了 C++14 中提供了相当惊人的编译时计算。
首先我需要Scott Schurr's str_const
presented at C++ Now 2012. This class is a compile-time string and discussed a bit in 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_month
它需要一个 str_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, "");
}
我在2015年9月6日编译这个程序,正好是这个月的第一个星期天
您需要 gcc 或 clang 来执行此操作。即使是最新的 VS-2015 也不符合规范 constexpr
足以在编译时进行这些计算。
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__
。
我正在尝试将 __DATE__
格式的月份 Mmm
转换为编译时的数字。我需要 gcc 和 MS VC 2012.
我的尝试:
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
在 MS VC
error C2970: 'month' : template parameter 'C0' : 'c0' : an expression involving objects with internal linkage cannot be used as a non-type argument
但在 ideone 上工作正常。
有没有办法让这个作品跨platform/compiler?
编辑
我需要 compile-time constant
所以 similar question 中的答案并没有多大帮助。例如,我需要减去两个日期(代码中的当前日期和某个日期),并在这两个日期之间的差异足够大时给出编译时错误。
首先:你确定你在编译时需要这个吗?如果 运行-time 是可以接受的,那很简单: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
);
免责声明:我只是在脑海中写下了这一点。现在可以用了,但谁知道呢,也许我弄错了 March。
事实上,如果您想获得更多乐趣*,我们可以对某些字符使用算术:
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" 我的意思是:被其他开发者讨厌
由于这些是常量,因此您可以将其与模板一起使用。例如,假设我们有一份合同工作将于 11 月结束,我们希望确保在合同结束后我们能以高利率被带回工作几天:
#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;
在这里玩得开心...
我的回答需要 C++14 和一些外部库,但证明了 C++14 中提供了相当惊人的编译时计算。
首先我需要Scott Schurr's str_const
presented at C++ Now 2012. This class is a compile-time string and discussed a bit in 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_month
它需要一个 str_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, "");
}
我在2015年9月6日编译这个程序,正好是这个月的第一个星期天
您需要 gcc 或 clang 来执行此操作。即使是最新的 VS-2015 也不符合规范 constexpr
足以在编译时进行这些计算。
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__
。