9

I would like to make the compilation of some files to fail if attempted after a certain date. The reason for this: I found a couple of Y2K38 bugs which I don't have time to fix right now, but would like to make a note of them and I think it would be nice if compilation of the module would just fail after, say, 2020. (I might be insane, but this code is 20 years old I suspect it might survive another 30)

MK.
  • 33,605
  • 18
  • 74
  • 111
  • 1
    Isn't the issue more that the program will stop running on 2038, not that it will stop compiling? – Justin Morgan Feb 02 '11 at 04:40
  • 8
    Who cares if you can't compile it after, say, 2020? The issue is whether or not there is compiled version still in production somewhere in 2038. Fix that shit, pronto, and get it deployed. – jason Feb 02 '11 at 04:40
  • 4
    Have you considered a #warning? I've used those before as little to-do's for the future. – whitey04 Feb 02 '11 at 04:42
  • 3
    Hopefully your program doesn't involve retirement planning or 30-year mortgages. – dan04 Feb 02 '11 at 04:46
  • if this was really important I'd fix it immediately; it's mostly non-important reporting stuff that nobody ever looks at. Do you always fix all the known bugs before shipping a release? Honestly? – MK. Feb 02 '11 at 04:57
  • 1
    Not a duplicate IMHO, but http://stackoverflow.com/questions/3385515/static-assert-in-c may solve your problem. – Chris Lutz Feb 02 '11 at 05:48
  • Actually, I can't get any of those static assertions to work for the `__DATE__[9] >= '2'` condition. Hmm... – Chris Lutz Feb 02 '11 at 06:07
  • 1
    What do you do, if the compiler in question has the Y2038 bug, too? – Jens Gustedt Feb 02 '11 at 08:54

5 Answers5

5

With GCC, you can do something like the following:

void __attribute__((error("Whoa. It's the future"))) whoa_the_future();

void check_for_the_future() {
    // "Feb  1 2011"
    const char *now = __DATE__;
    if (now[9] >= '2')
        whoa_the_future();
}

The way this works is that the error attribute tells GCC to generate a compile-time error if any calls to that function are left in the code after all of GCC's constant-folding, dead-code elimination, and similar passes have run. Since DATE is a compile-time constant, GCC can evaluate the if statement at compile time and remove the call.

At least one downside is that this depends on GCC's optimization passes, and so it won't work at gcc -O0

Honestly, you might be better off just adding a runtime check somewhere and failing fast.

nelhage
  • 2,734
  • 19
  • 14
5

Instead of dealing with the awkward format of the __DATE__ macro, why not roll your own?

gcc -DTHIS_YEAR=`/bin/date +%Y` yourprogram.c

Then your code can use expressions like #if THIS_YEAR >= 2020.

dan04
  • 87,747
  • 23
  • 163
  • 198
  • oh, I like this... sort of. A little too fragile. The build system is likely to change in the next 10 years and this will get broken. – MK. Feb 02 '11 at 05:27
  • 1
    @MK: you can also have an `#if` that generates an `#error` if `THIS_YEAR` isn't defined, which will ensure build system changes maintain this functionality. – Tony Delroy Feb 02 '11 at 05:49
  • If I encountered that in a build system, I would simply add `-DTHIS_YEAR=2011` to the `CFLAGS`... :-) – R.. GitHub STOP HELPING ICE Feb 02 '11 at 05:56
5

Here's a horrible solution:

  1. In your project's general-purpose header directory, run the following (Python) script:

    #!/usr/bin/python
    
    months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
              'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
    
    cutoff = 2020
    #safety = 2025
    
    for year in range(2011, cutoff):
        for month in months:
            for day in range(1, 32):
                t = open("%s %2d %d" % (month, day, year), "w")
                t.write("\n");
                t.close()
    
    #for year in range(2011, cutoff):
    #    for month in months:
    #        for day in range(1, 32):
    #            t = open("%s %2d %d" % (month, day, year), "w")
    #            t.write("#error \"Too old\"\n");
    #            t.close()
    

    Uncomment the commented-out lines to produce better diagnostic messages.

  2. In the files that need to error after the cutoff date, use this:

    #include __DATE__
    

I dare you to use this in production code.

Chris Lutz
  • 73,191
  • 16
  • 130
  • 183
1

__DATE__ is not the proper thing for such a goal:

If the date of translation is not available, an implementation-defined valid date shall be supplied.

Any future broken C compiler that still only implements C99 :) and not any of its followers may fix the date to "Jan  1 1970" or have it wrap once beyond the fatal date in 2038.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • Theoretically, yes, this is true (and I laugh at the phrase "_still only_ implements C99" - that'll be the day), but in practice no modern compiler's internal date is going to fail before 2038, and since the OP wants a warning well before that date this should work. But +1 for technical correctness. – Chris Lutz Feb 02 '11 at 08:02
  • No that is not only theoretical. Somebody who will still try to compile a version of code where this ain't fixed at that time is very likely to use a then broken compiler, too. In particular, if the compiler implementor uses the same strategy and the compiler's `__DATE__` wraps (which is legal). So in essence, `__DATE__` is the wrong answer to a wrong question. – Jens Gustedt Feb 02 '11 at 08:52
0

A more generic solution that should work with most compilers. It depends a bit on the format of the DATE preprocessor directive

#define DIGIT(ch) (((ch)<'0'||(ch)>'9') ? 0 : ((ch)-'0'))
#define YEAR (1000*DIGIT(__DATE__[7])+100*DIGIT(__DATE__[8])+10*DIGIT(__DATE__[9])+DIGIT(__DATE__[10]))

#ifdef YEAR-2020>0
#error too old
#endif
steve
  • 5,870
  • 1
  • 21
  • 22
  • 2
    Array subscripting does not work at the preprocessor level. Sorry. – R.. GitHub STOP HELPING ICE Feb 02 '11 at 05:08
  • @R I think it does. I tried his code and YEAR does get the correct value of 2011, but it is not clear how to use it (to me). – MK. Feb 02 '11 at 05:09
  • @MK - `YEAR` has the correct value in compilation, but at preprocessing time cannot be used to signal an `#error` – Chris Lutz Feb 02 '11 at 05:18
  • @Chris yes, I just wasn't parsing what @R said. Actually, I still don't fully understand what he meant, but yes, looks like this approach is incorrect. – MK. Feb 02 '11 at 05:28
  • @steve - Not for me. A) You're using `#ifdef` when you obviously mean `#if`. B) With `#ifdef` for me the error triggers (and I'm not from a time traveller.) C) `#if` directives cannot process strings, even string subscripts. – Chris Lutz Feb 02 '11 at 05:44
  • @Chris: I don't mean #if, I mean #ifdef as this will either evaluate to 1 or 0 – steve Feb 02 '11 at 05:48
  • @steve - That's not the proper syntax for `#ifdef`, and when I copy-and-paste your code through my compiler (`gcc version 4.0.1 (Apple Inc. build 5470) (Aspen 5470.3)`) I get `:5:2: error: #error too old`. – Chris Lutz Feb 02 '11 at 05:56
  • I get "y.c:6:1: error: token ""Feb 2 2011"" is not valid in preprocessor expressions" on gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-48) – MK. Feb 02 '11 at 06:08