【问题标题】:How to split __DATE__ and __TIME__ macros into individual components for variable declarations?如何将 __DATE__ 和 __TIME__ 宏拆分为单独的组件以进行变量声明?
【发布时间】:2017-10-23 22:00:27
【问题描述】:

我有以下结构(在嵌入式系统上):

struct Calib_Time_struct
{
    uint16_t    year;
    uint16_t    month;
    uint16_t    day;
    uint16_t    hour;
    uint16_t    minute;
    uint16_t    seconds;
};

我有一个“默认”值数组,我需要在其中插入时间值:

struct Calib_Table_struct
{
    unsigned int                    table_id;           //!< Table identifier.
    char                            group_name[CAL_TBL_MAX_GROUP_NAME_LENGTH];
    unsigned int                    channel_number;     //!< Channel number within the group.
    float                           floor_value;        //!< Minimum value for a channel.
    unsigned int                    size;               //!< Number of elements in the table.
    struct Calib_Time_struct    modification_date;  //!< Date of modification.
};
static const struct Calib_Time_struct default_values[] =
{
    // Table 0
    {
        .table_id = 0U,
        .group_name = "ADC",
        .channel_number = 0U,
        .floor_value = 0.0f,
        .size = 1,
        .modification_date =
        {
            .year       = /* extract from __DATE__ macro */;
        },
    },
    //...
};

我想在__DATE__宏中填写“modification_date”成员的年月日。

有没有办法做到这一点? (任何黑客攻击?)

可以将类似的方法或技巧应用于__TIME__ 宏吗?

动机是允许编译器(在构建服务器上)自动插入值,而不是让开发人员这样做。我们团队中有许多开发人员,并使用构建服务器来制作“官方”构建,然后交付给我们团队以外的人。

数据将附加到可执行文件并存储在闪存中,由引导加载程序下载到内存中。

默认数组中有很多(超过 80 个)表。

我的工具:
IAR Systems IDE 和编译器:7.4
使用 ARM Cortex-A8 的嵌入式系统平台。

语言:主要用 C 语言,但可能对 C++ 语言的人有用。

【问题讨论】:

  • default_values 不是常量表达式,因此您可以在程序开始时动态填充它,不是吗? (不完美的性能明智,ik)
  • default_values 是一个常量表达式,为了简单更新,这个结构在main() 之前和里面都没有修改。
  • 不,不是。它只是一个 const 对象,编译不依赖于它的值是否可用。您可以简单地使用函数调用来初始化它,该函数调用使用 C 或 C++ 而不是 PP 解析 __DATE__ 字符串。我不知道您所说的“更新简单”是什么意思,您当然可以将编译时间作为您的要求,但目前没有说明。

标签: c++ c date macros c-preprocessor


【解决方案1】:

C 规范保证 __DATE__ 将是形式的字符串文字

MMM DD YYYY

如果日期是单个字符长,则日期的第一个数字为空格。这意味着您可以像这样进行残忍和不寻常的事情,从技术上讲,这不是可移植的(预处理器不必使用与目标相同的字符编码系统),但应该可以工作:

#define YEAR_CHAR 7
#define YEAR  (\
                ((__DATE__)[YEAR_CHAR + 0] - '0') * 1000 + \
                ((__DATE__)[YEAR_CHAR + 1] - '0') * 100  + \
                ((__DATE__)[YEAR_CHAR + 2] - '0') * 10   + \
                ((__DATE__)[YEAR_CHAR + 3] - '0') * 1      \
              )

__TIME__ 宏保证具有格式

hh:mm:ss

所以你可以类似地做一些像这样可怕的事情:

#define HOUR_CHAR 0
#define HOUR (\
                ((__TIME__)[HOUR_CHAR + 0] - '0') * 10 + \
                ((__TIME__)[HOUR_CHAR + 1] - '0') * 1    \
             )

获取小时、分钟或秒。

如果你有一个 C++14 兼容的编译器,你可以使用 constexpr 函数来计算这个不那么可怕:

constexpr int compilationYear() {
    const int kYearChar = 7;
    const int kNumYearChars = 4;

    int result = 0;
    for (int i = yearChar + kNumYearChars - 1, multiplier = 1;
         i >= kYearChar;
         i--, multiplier *= 10) {
        result += (__DATE__[i] - '0') * multiplier;
    }

    return result;
}

然后,您可以指定 compilationYear() 的值,并在编译时评估该函数。

【讨论】:

  • @zneak 你知道,我没有检查。我认为这是从 C 规范中整合的内容之一,但我还没有证实这一点。
  • 很可能。我也不知道。
【解决方案2】:

您可以简单地使用运行时调用 __DATE__ 宏的解析函数来初始化它(»Mmm dd yyyy«,这意味着年份的字符位于已知位置):

int extractYearFromDateMacro() {
    return 1000 * (__DATE__[7] - '0') + 100 * (__DATE__[8] - '0') + 10 * (__DATE__[9] - '0') + (__DATE__[10] - '0');
}

    .modification_date =
    {
        .year       = extractYearFromDateMacro()
    },

从 C++11 开始,您可以在前面添加 constexpr 并让编译器对其进行评估。 (也用于常量表达式)

Demo

同样的方法也可以应用于__TIME__ 宏(“hh:mm:ss”)。

如果你只连接表达式,使用预处理器甚至不能保证你编译时评估。这些仍然可以在运行时进行评估。

extractYearFromDateMacro 上面的可能会像 PP 扩展的结果一样被恒定折叠。

相比之下,使用constexpr 方法将保证编译时评估。

【讨论】:

  • __ DATE __ 产生“2019 年 7 月 9 日”,arduino ide 1.8.9
  • @Codebeat 根据其他答案,格式得到保证,因此您的编译器不符合要求,您最好提交错误报告。出于实际目的,我相信您可以相应地调整给定的功能。由于在您的情况下它似乎是可变长度的,我会将其保存到 std::string 并在其 size() 的帮助下从末尾开始索引。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-27
  • 2014-04-23
相关资源
最近更新 更多