Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to split __DATE__ and __TIME__ macros into individual components for variable declarations?

Tags:

I have the following structure (on an embedded system):

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

I have a "default" values array, in which I need to insert time values:

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 */;
        },
    },
    //...
};

I would like to fill in the year, month, and day of the "modification_date" member from the __DATE__ macro.

Is there a method to do this? (Any hacks?)

Can a similar method or hack be applied to the __TIME__ macro?

The motivation is to allow the compiler (on a build server) to plug in the values automatically, rather than having a developer do this. We have many developer's on the team, and use the build server to make "official" builds that are delivered to people outside our team.

The data will be attached to the executable and stored in Flash, downloaded by a bootloader into memory.

There are a lot (over 80) tables in the default array.

My tools:
IAR Systems IDE & Compiler: 7.4
Embedded Systems platform using ARM Cortex-A8.

Languages: primarily in C, but may be useful for C++ language folks.

like image 830
Thomas Matthews Avatar asked Oct 23 '17 22:10

Thomas Matthews


2 Answers

The C spec guarantees that __DATE__ will be a string literal of the form

MMM DD YYYY

with the first digit of the date being whitespace if the date is a single character long. This means you could do Cruel and Unusual Things like this, which technically speaking isn't portable (the preprocessor doesn't have to use the same character encoding system as the target), but should probably work:

#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      \
              )

The __TIME__ macro is guaranteed to have the form

hh:mm:ss

and so you could similarly do something horrible like this:

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

to get the hour, or the minute, or the second.

If you have a C++14-compliant compiler, you could use constexpr functions to compute this less horribly:

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

You could then assign something the value of compilationYear() and will evaluate that function at compile-time.

like image 133
templatetypedef Avatar answered Nov 15 '22 04:11

templatetypedef


You can simply initialize it using a runtime call to a parsing function for the __DATE__ macro (»Mmm dd yyyy«, which means the chars for the year are at known positions):

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

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

From C++11 you can add constexpr in front and have the compiler evaluate it. (also for use in constant expressions)

Demo

The same method can be applied to the __TIME__ macro ("hh:mm:ss") as well.

Using the preprocessor won’t even guarantee you compile time evaluation if you only concatenate expressions. These may still be evaluated at runtime.

extractYearFromDateMacro above will probably be constant folded the same way the result of PP expansion would.

In contrast using the constexpr approach will guarantee compile time evaluation.

like image 40
Darklighter Avatar answered Nov 15 '22 05:11

Darklighter