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