0
votes

I am completely unexperienced in C++, I use C all the time. In my recent hobby project I need to mix in a C++ library with my C code thus forcing me to have a C++ main.cpp.

I am right now meddling my C code to compile as C++. This even works pretty well except for one problem I simply cannot solve.

This is an interrupt service incrementing my unix time variable every second and also doing a conversion into spli-up time format afterwards. Since this happens in an interrupt I set the variables as volatile.

volatile time_t UNIX_TIME = 0;
volatile struct tm TIME_CUR_LOCALTIME;  

if (htim == &htim2){
    UNIX_TIME++;
    TIME_CUR_LOCALTIME = *localtime(&UNIX_TIME);
}

From library time.h:

struct tm *localtime (const time_t *_timer);

With gcc this compiles flawless.

g++ is a different story. It gives me the error:

error: passing 'volatile tm' as 'this' argument discards qualifiers [-fpermissive]

I tried multiple casts and stuff, the only way this starts working by itself is when I drop both volatile qualifiers. But I don't want that.

What is the correct way to get this working? I am out of ideas.

2
"I tried multiple casts and stuff" - which casts, and what stuff? - underscore_d
You should not be using localtime in an ISR - it isn't threadsafe. Use localtime_r instead. - Paul Sanders
@underscore_d The usual stuff which is enough to convince gcc, but g++ sees right through me ;-) I tried for example TIME_CUR_LOCALTIME = (volatile struct tm)*localtime((const time_t*)&UNIX_TIME); Just trying to cast things that they will fit, but this doens't work here. - nEmai
@PaulSanders Thanks, I will have a look into that. - nEmai

2 Answers

0
votes

Copy the time first into a non-volatile instance:

time_t t = UNIX_TIME;
tm local = *localtime(&t);
TIME_CUR_LOCALTIME.tm_sec   = local.tm_sec;
TIME_CUR_LOCALTIME.tm_min   = local.tm_min;
TIME_CUR_LOCALTIME.tm_hour  = local.tm_hour;
TIME_CUR_LOCALTIME.tm_mday  = local.tm_mday;
TIME_CUR_LOCALTIME.tm_mon   = local.tm_mon;
TIME_CUR_LOCALTIME.tm_year  = local.tm_year;
TIME_CUR_LOCALTIME.tm_wday  = local.tm_wday;
TIME_CUR_LOCALTIME.tm_yday  = local.tm_yday;
TIME_CUR_LOCALTIME.tm_isdst = local.tm_isdst;
0
votes

Neither the C Standard nor the C++ Standard makes any attempt to distinguish between objects which will be modified in ways a compiler would have no reason to expect, those which will only be written in ways the compiler understands but might also be read in ways it would have no reason to expect, and those which, while declared volatile, will in fact never be accessed in ways the compiler wouldn't expect (the latter situation may arise with libraries that will sometimes be used in systems where things are accessed via outside means and sometimes used in systems where they aren't). All non-atomic objects that may be accessed in ways the compiler wouldn't expect must be qualified volatile, and because--from the Standard's point of view--all objects that are qualified volatile might be read or written in arbitrary outside means, any access to such an object without a volatile qualifier invokes Undefined Behavior, without regard for whether any outside access actually occurs. The authors of the Standard probably recognized that since people wishing to sell compilers would handle such cases sensibly without regard for whether they are required to do so, there was no need for the Standard to address such cases. Not all compiler writers, however, are bound by that principle.

The best way to achieve the behavior at issue here would probably be to copy UNIX_TIME to a couple of ordinary objects, ensuring that they match and repeating the read attempts if not, and then pass the address of that to any functions that would need the address of an object holding the time. As noted elsewhere, one should avoid functions like localtime that use static buffers, use a function like localtime_r, but what's important if one wants to shut up the compiler is to copy the object somewhere else before use.