April 02, 2022

Consider the following code:

import std;

auto logVariadic(T...)(T x,int line=__LINE__,string file=__FILE__) {
    writeln(file,":",line," ",x);
}

int variadicCnt;
auto logVariadicWrapper(T...)(T x, int line=__LINE__, string file=__FILE__) {
 variadicCnt++;
 logVariadic(x,line,file);
}

auto args(T...)(T x) {
    static struct Args(T...) {
        static foreach(i, t; T) {
            mixin(t, " v",i,";");
        }
    }
    return Args!T(x);
}
auto log(Args)(Args args, int line=__LINE__, string file=__FILE__) {
    writeln(file,":",line," ",args.tupleof);
}
int cnt;
auto logWrapper(Args)(Args args, int line=__LINE__, string file=__FILE__) {
 cnt++;
 log(args,line,file);
}

void main()
{
    logVariadic("Hello ",1234);
    logVariadicWrapper(1234, " is a number."); //produces wrong line number and adds line number and file name at the end
    writeln(variadicCnt);
    log(args("Hello ",1234));
    logWrapper(args(1234, " is a number."));
    writeln(cnt);
}

Produces output:
onlineapp.d:32 Hello 1234
onlineapp.d:10 1234 is a number.33onlineapp.d
1
onlineapp.d:35 Hello 1234
onlineapp.d:36 1234 is a number.
1

Any other ways to be able to achieve the same without double wrapping arguments like above? I guess the above is okay but I thought I would see other options.
(I know that one can pass line and file as template arguments, but that produces a new instance of log or logWrapper per use).