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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With