string file = __FILE__ considered harmful (and solution)

bauss jj_1337 at live.dk
Thu May 31 11:14:54 UTC 2018


On Wednesday, 30 May 2018 at 14:40:50 UTC, Steven Schveighoffer 
wrote:
> On 5/30/18 4:27 AM, FeepingCreature wrote:
>> There's a very common idiom where in order to report line 
>> numbers of an error or a log line at the callsite of a 
>> function, you pass __FILE__ and __LINE__ as default parameters:
>> 
>> void foo(string file = __FILE__, size_t line = __LINE__);
>> 
>> What's wrong with this?
>> 
>> Say you add a string parameter, such as
>> 
>> void foo(string msg, string file = __FILE__, size_t line = 
>> __LINE__);
>> 
>> foo("Hello World");
>> 
>> Now when you accidentally grab an old version of the library, 
>> your new code will still run, but it will believe that it's 
>> being called from file "Hello World", line 15. Not good.
>> 
>> Luckily there's a fix. Just stick this in some common header 
>> file in your project:
>> 
>> struct CallerInfo
>> {
>>    string file;
>>    size_t line;
>> }
>> 
>> void foo(string msg, CallerInfo caller = CallerInfo(__FILE__, 
>> __LINE__));
>> 
>> Now you cannot accidentally invoke foo with a string, or in 
>> fact any type except another instance of CallerInfo.
>
> Awesome idea! Unfortunately, it doesn't work. The __FILE__ and 
> __LINE__ there are not from the caller, but from the line that 
> defines foo.
>
> See here: https://run.dlang.io/is/siz9YZ
>
>> At which point we can shorten this to CallerInfo caller = 
>> __CALLER__, and be forward compatible for additional 
>> information about the callsite, such as, say, attributes of 
>> the calling function.
>
> Hm.. I don't like this too much. Adding more magic to the 
> compiler seems unnecessary.
>
> But if we fixed the behavior that causes your idea not to work, 
> then we could probably easily define a function like so:
>
> CallerInfo __CALLER__(string file = __FILE__, size_t line = 
> __LINE__)
> {
>     return CallerInfo(file, line);
> }
>
> -Steve

Instead of these hack keywords.

Perhaps a __traits() in the compiler with the information would 
be better suited like:


void foo()
{
     enum caller = __traits(getCaller);

     ....
}

getCaller would return a compile-time struct with additional 
information about the current module and the module/function it 
was called from.

Alternatively you can use the following traits.

true/false as secondary argument for whether it should be its 
current module or the call module/function etc. This argument 
should be optional and when omitted should default to the callee.

     __FILE__ -- __traits(getFile);
     __FILE_FULL_PATH__ -- __traits(getFilePath);
     __MODULE__ -- __traits(getModule);
     __LINE__  -- __traits(getLine);
     __FUNCTION__ -- __traits(getFunction);
     __PRETTY_FUNCTION__ -- __traits(getPrettyFunction);


More information about the Digitalmars-d mailing list