views:

960

answers:

6

Is there a way to get the C/C++ preprocessor or a template or such to mangle/hash the __FILE__ and __LINE__ and perhaps some other external input like a build-number into a single short number that can be quoted in logs or error messages?

(The intention would be to be able to reverse it (to a list of candidates if its lossy) when needed when a customer quotes it in a bug report.)

A: 

Well, if you're displaying the message to the user yourself (as opposed to having a crash address or function be displayed by the system), there's nothing to keep you from displaying exactly what you want.

For example:

typedef union ErrorCode {
    struct {
        unsigned int file: 15;
        unsigned int line: 12; /* Better than 5 bits, still not great
                                  Thanks commenters!! */
        unsigned int build: 5;
    } bits;
    unsigned int code;
} ErrorCode;

unsigned int buildErrorCodes(const char *file, int line, int build)
{
    ErrorCode code;
    code.bits.line=line   & ((1<<12) - 1);
    code.bits.build=build & ((1<< 5) - 1);
    code.bits.file=some_hash_function(file) & ((1<<15) - 1);

    return code.code;
}

You'd use that as

buildErrorCodes(__FILE__, __LINE__, BUILD_CODE)

and output it in hex. It wouldn't be very hard to decode...

(Edited -- the commenters are correct, I must have been nuts to specify 5 bits for the line number. Modulo 4096, however, lines with error messages aren't likely to collide. 5 bits for build is still fine - modulo 32 means that only 32 builds can be outstanding AND have the error still happen at the same line.)

Mike G.
You must have some seriously short source files for __LINE__ to fit in 5 bits.
Michael Burr
He suggests C/C++, but his own projects could be written in Python, (ergo very little code needed :) Just kidding, of course, Mike B's comment is spot on. Mike G, you should revise your answer.
ΤΖΩΤΖΙΟΥ
You are both absolutely correct. Edited to make line 12 bits rather than 5, and explicitly modulo the line #.
Mike G.
+2  A: 

You will have to use a function to perform the hashing and create a code from __LINE__ and __FILE__ as the C preprocessor is not able to do such complex tasks.

Anyway, you can take inspiration by this article to see if a different solution can be better suited to your situation.

Remo.D
A: 

Well... you could use something like:

((*(int*)__FILE__ && 0xFFFF0000) | version << 8 | __LINE__ )

It wouldn't be perfectly unique, but it might work for what you want. Could change those ORs to +, which might work better for some things.

Naturally, if you can actually create a hashcode, you'll probably want to do that.

davenpcj
Frank Szczerba
A: 

I needed serial valuse in a project of mine and got them by making a template that specialized on __LINE__ and __FILE__ and resulted in an int as well as generating (as compile time output to stdout) a template specialization for it's inputs that resulted in the line number of that template. These were collected the first time through the compiler and then dumped into a code file and the program was compiled again. That time each location that the template was used got a different number.

(done in D so it might not be possible in C++)

template Serial(char[] file, int line)
{
    prgams(msg, 
    "template Serial(char[] file : \"~file~"\", int line : "~line.stringof~")"
      "{const int Serial = __LINE__;");
    const int Serial = -1;
}
BCS
A: 

A simpler solution would be to keep a global static "error location" variable.

#ifdef DEBUG
#define trace_here(version) printf("[%d]%s:%d {%d}\n", version, __FILE__, __LINE__, errloc++);
#else
#define trace_here(version) printf("{%lu}\n", version<<16|errloc++);
#endif

Or without the printf.. Just increment the errloc everytime you cross a tracepoint. Then you can correlate the value to the line/number/version spit out by your debug builds pretty easily.

You'd need to include version or build number, because those error locations could change with any build.

Doesn't work well if you can't reproduce the code paths.

davenpcj
A: 

__FILE__ is a pointer into the constants segment of your program. If you output the difference between that and some other constant you should get a result that's independent of any relocation, etc:

extern const char g_DebugAnchor;
#define FILE_STR_OFFSET (__FILE__ - &g_DebugAnchor)

You can then report that, or combine it in some way with the line number, etc. The middle bits of FILE_STR_OFFSET are likely the most interesting.

Frank Szczerba