Thread overview
Template context
Jan 07, 2007
Daniel Keep
Jan 07, 2007
Daniel Keep
Jan 08, 2007
Marcin Kuszczak
Jan 08, 2007
Daniel Keep
Jan 08, 2007
Aarti_pl
Re: Template context - OT variants
Jan 08, 2007
Aarti_pl
Jan 08, 2007
Bill Baxter
Jan 07, 2007
Georg Wrede
January 07, 2007
Ok, so I've just given up on finding any reasonable way to make this any easier:

> trace("module.function_name", __FILE__, __LINE__, "fooing the bar");

The problem is, as I'm sure you're aware, that __FILE__ and __LINE__ are expanded by the tokeniser, NOT the parser.  Therefore, I propose the following:

Templates all gain (either implicitly or via a specially named argument) an object called "context", which represents the context within which the template was instantiated.  It should have the following compile-time properties:

* context.line: line on which the template was instantiated.
* context.file: file in which the template was instantiated.
* context.symbol: the full name of the symbol in which etc.

For example:

> module foo;
>
> void trace(T_Args...)(T_Args args...)
> {
>     writef("%s:%d (%s): ", context.file, context.line, context.symbol);
>     foreach( arg ; args )
>         writef(arg);
>     writefln();
> }
>
> void main()
> {
>     trace("Hi, ma!");
> }

Would produce the output:

foo.d:13 (foo.main): Hi, ma!

If any templates want to propogate their context to other templates, they can just pass it manually.  Currently, passing this information around is painful (and we can't even get the symbol part AFAIK).

Pretty please, Mr. Bright?  *puppy dog eyes*

	-- Daniel

P.S.  We could also (with a *teensy* modification to Phobos) do this:

> module bar;
>
> void raise(T_Exception, T_Args...)(T_Args args)
> {
>     auto e = new T_Exception(args);
>     e.file = context.file;
>     e.line = context.line;
>     e.symbol = context.symbol;
>     throw e;
> }
>
> // blah
>
> void main()
> {
>     raise!(GasLeakException)("Houston, we have a problem...");
> }

Error: bar.d line 16 in bar.main: Houston, we have a problem...
January 07, 2007
Daniel Keep wrote:
> 
> Ok, so I've just given up on finding any reasonable way to make this any easier:
> 
>  > trace("module.function_name", __FILE__, __LINE__, "fooing the bar");
> 
> The problem is, as I'm sure you're aware, that __FILE__ and __LINE__ are expanded by the tokeniser, NOT the parser.  Therefore, I propose the following:
> 
> Templates all gain (either implicitly or via a specially named argument) an object called "context", which represents the context within which the template was instantiated.  It should have the following compile-time properties:
> 
> * context.line: line on which the template was instantiated.
> * context.file: file in which the template was instantiated.
> * context.symbol: the full name of the symbol in which etc.
> 
> For example:
> 
>  > module foo;
>  >
>  > void trace(T_Args...)(T_Args args...)
>  > {
>  >     writef("%s:%d (%s): ", context.file, context.line, context.symbol);
>  >     foreach( arg ; args )
>  >         writef(arg);
>  >     writefln();
>  > }
>  >
>  > void main()
>  > {
>  >     trace("Hi, ma!");
>  > }
> 
> Would produce the output:
> 
> foo.d:13 (foo.main): Hi, ma!
> 
> If any templates want to propogate their context to other templates, they can just pass it manually.  Currently, passing this information around is painful (and we can't even get the symbol part AFAIK).
> 
> Pretty please, Mr. Bright?  *puppy dog eyes*
> 
>     -- Daniel
> 
> P.S.  We could also (with a *teensy* modification to Phobos) do this:
> 
>  > module bar;
>  >
>  > void raise(T_Exception, T_Args...)(T_Args args)
>  > {
>  >     auto e = new T_Exception(args);
>  >     e.file = context.file;
>  >     e.line = context.line;
>  >     e.symbol = context.symbol;
>  >     throw e;
>  > }
>  >
>  > // blah
>  >
>  > void main()
>  > {
>  >     raise!(GasLeakException)("Houston, we have a problem...");
>  > }
> 
> Error: bar.d line 16 in bar.main: Houston, we have a problem...

Honestly, I didn't like the proposal at first... but when I started to respond, it rather abruptly grew on me.  At least some straightforward means of achieving this behavior would be quite welcome.  Perhaps 'context' should be '_context' in the vein of _arguments and _args though.  Getting the symbol, etc, wouldn't really be all that difficult.  Just define _context as a namespace of constants available during template instantiation.

-- Chris Nicholson-Sauls
January 07, 2007
Chris Nicholson-Sauls wrote:
> Honestly, I didn't like the proposal at first... but when I started to respond, it rather abruptly grew on me.  At least some straightforward means of achieving this behavior would be quite welcome.  Perhaps 'context' should be '_context' in the vein of _arguments and _args though.  Getting the symbol, etc, wouldn't really be all that difficult.  Just define _context as a namespace of constants available during template instantiation.
> 
> -- Chris Nicholson-Sauls

I don't care *what* it's called, or if we have to jump through a hoop, just so long as we can get to something like it :)

	-- Daniel
January 07, 2007
Daniel Keep wrote:
> 
> Ok, so I've just given up on finding any reasonable way to make this any easier:
> 
>  > trace("module.function_name", __FILE__, __LINE__, "fooing the bar");
> 
> The problem is, as I'm sure you're aware, that __FILE__ and __LINE__ are expanded by the tokeniser, NOT the parser.  Therefore, I propose the following:
> 
> Templates all gain (either implicitly or via a specially named argument) an object called "context", which represents the context within which the template was instantiated.  It should have the following compile-time properties:
> 
> * context.line: line on which the template was instantiated.
> * context.file: file in which the template was instantiated.
> * context.symbol: the full name of the symbol in which etc.
> 
> For example:
> 
>  > module foo;
>  >
>  > void trace(T_Args...)(T_Args args...)
>  > {
>  >     writef("%s:%d (%s): ", context.file, context.line, context.symbol);
>  >     foreach( arg ; args )
>  >         writef(arg);
>  >     writefln();
>  > }
>  >
>  > void main()
>  > {
>  >     trace("Hi, ma!");
>  > }
> 
> Would produce the output:
> 
> foo.d:13 (foo.main): Hi, ma!
> 
> If any templates want to propogate their context to other templates, they can just pass it manually.  Currently, passing this information around is painful (and we can't even get the symbol part AFAIK).
> 
> Pretty please, Mr. Bright?  *puppy dog eyes*
> 
>     -- Daniel
> 
> P.S.  We could also (with a *teensy* modification to Phobos) do this:
> 
>  > module bar;
>  >
>  > void raise(T_Exception, T_Args...)(T_Args args)
>  > {
>  >     auto e = new T_Exception(args);
>  >     e.file = context.file;
>  >     e.line = context.line;
>  >     e.symbol = context.symbol;
>  >     throw e;
>  > }
>  >
>  > // blah
>  >
>  > void main()
>  > {
>  >     raise!(GasLeakException)("Houston, we have a problem...");
>  > }
> 
> Error: bar.d line 16 in bar.main: Houston, we have a problem...

It's a shame that you're such a slow thinker, we could've had this in 1.0 if you only had come out with it like 3 months ago.

Just kidding, this is an excellent proposition, definitely 1.1 material!
Both of these ideas, actually.

I've always felt that _FILE_ and _LINE_ taste, how should I say, preprocessorish or see(plusplus)ish. They're so 1980's.
January 08, 2007
Chris Nicholson-Sauls wrote:

> Perhaps 'context' should be '_context' in the vein of _arguments and _args though.  Getting the symbol, etc, wouldn't really be all that difficult.  Just define _context as a namespace of constants available during template instantiation.


ohhh.. please... do not make another strange _context object!

Personally I think that passing information about variadic arguments of function with _arguments and _argptr is black side of D... To get it work properly IMHO there should be build in variant type (which is really usefull in some cases) and arguments to variadic functions could be sent in standard way:

void varfunction(variant[] vals ...); // Same syntax like D typesafe
variadic functions.

-----------------------------

In case of template instatiation context the idea is nice and very usefull, but I would rather propose property like syntax for getting context:

void trace(T_Args...)(T_Args args...) {
        writef("%s:%d (%s): ", trace.context.file, trace.context.line,
trace.context.symbol);
        foreach( arg ; args )
                writef(arg);
        writefln();
        }

Advantages:
1. Properties for functions already works (nothing interesting, but compiles
without problem with DMD), so it is nothing new for compiler.
2. It could work also for normal functions
3. Makes less pollution of namespace


-- 
Regards
Marcin Kuszczak (Aarti_pl)
-------------------------------------
Ask me why I believe in Jesus - http://zapytaj.dlajezusa.pl (en/pl)
Doost (port of few Boost libraries) - http://www.dsource.org/projects/doost/
-------------------------------------

January 08, 2007
Marcin Kuszczak wrote:
> ohhh.. please... do not make another strange _context object!
> 
> Personally I think that passing information about variadic arguments of
> function with _arguments and _argptr is black side of D... To get it work
> properly IMHO there should be build in variant type (which is really
> usefull in some cases) and arguments to variadic functions could be sent in
> standard way:
> 
> void varfunction(variant[] vals ...); // Same syntax like D typesafe
> variadic functions.

I've never liked the "magic" _arguments and _argptr.  I always thought they should be explicitly named.

Then again, I don't think a variant type is the answer.  We have std.boxer.Box, but I think there must be a better solution for variadic functions.  Perhaps some special construct for talking about a function's call stack in a portable way...

> In case of template instatiation context the idea is nice and very usefull,
> but I would rather propose property like syntax for getting context:
> 
> void trace(T_Args...)(T_Args args...) {
>         writef("%s:%d (%s): ", trace.context.file, trace.context.line,
> trace.context.symbol);
>         foreach( arg ; args )
>                 writef(arg);
>         writefln();
>         }

> int foo()()
> {
> 	return foo.context.line;
> }

What does that do?  If you can't see what I'm getting at, try this:

> class Foo
> {
>     int line()()
>     {
>         return line.context.line;
>     }
> }

Since we can "call" a parameter-less function without putting in the parens, how do we get to the context?  Making trailing '.'s suppress the function call would make the above style functions even less like real lvalues...

> Advantages:
> 1. Properties for functions already works (nothing interesting, but compiles
> without problem with DMD), so it is nothing new for compiler.
> 2. It could work also for normal functions
> 3. Makes less pollution of namespace

The above are true, but I think the syntax could be borderline. Originally I thought that there could be some kind of special argument in the template...

> template tFoo(tArg1, tArg2, context) { }

But it just seemed... icky.  Hmm... what about...

> void trace(T_Args...)(T_Args args...) {
>     writef("%s:%d (%s): ", context(trace).file,
>         context(trace).line,
>         context(trace).symbol);
>     foreach( arg ; args )
>         writef(arg);
>     writefln();
> }

How does that one sit?

	-- Daniel
January 08, 2007
Daniel Keep napisał(a):

> 
>> int foo()()
>> {
>>     return foo.context.line;
>> }
> 
> What does that do?  If you can't see what I'm getting at, try this:
> 
>  > class Foo
>  > {
>  >     int line()()
>  >     {
>  >         return line.context.line;
>  >     }
>  > }
> 
> Since we can "call" a parameter-less function without putting in the parens, how do we get to the context?  Making trailing '.'s suppress the function call would make the above style functions even less like real lvalues...
> 

Please notice that problem already exists in current implementation - that's nothing new in fact:

class Foo {
	int init() {
		return init.init; //threat init as property
	}
}

This code works - dmd threats init as function call not as function itself.

To disambiguate it would be probably enough to add parenthesis after init (line in your example). It could be rule for function properties:

class Foo {
	int init() {
		return init().init; //threat init() as function
	}
}


> 
>  > template tFoo(tArg1, tArg2, context) { }
> 
> But it just seemed... icky.  Hmm... what about...
> 
>  > void trace(T_Args...)(T_Args args...) {
>  >     writef("%s:%d (%s): ", context(trace).file,
>  >         context(trace).line,
>  >         context(trace).symbol);
>  >     foreach( arg ; args )
>  >         writef(arg);
>  >     writefln();
>  > }
> 
> How does that one sit?

I would rather propose another alternative, but not to suppress my previous proposition. I see it rather as explication of my previous concept, but it could be implemented independently.

There is special keyword in language which points to current object of class. It's called "this" and is quite well known ;-)

Why not to extend this idea for other types, e.g. functions. In case of function existing keyword "function" could be "pointer" (not literally, but semantically) to current function. So that you could write:

class Foo {
	int line()() {
		return function.file.line;
	}
}


Advantages:
1. This syntax would be sometimes more convenient as it is independent of function name
2. No new keywords
3. Consistent and expandable

Regarding point 3 of advantages I mean new keyword which could be introduced later e.g. "program". This keyword would keep properties for program as a whole.

With this extension it would be possible to replace existing __FILE__, __LINE__ completely. Imagine this:

void main() {
	writefln(program.file.name, program.file.line);
}

and others nice things as e.g. getting address of main() function:

	program.main

which should allow to recursively call main function if anyone needs such a functionality ;-)


Best Regards
Marcin Kuszczak
Aarti_pl
January 08, 2007
Daniel Keep napisał(a):

>>
>> void varfunction(variant[] vals ...); // Same syntax like D typesafe
>> variadic functions.
> 
> I've never liked the "magic" _arguments and _argptr.  I always thought they should be explicitly named.
> 
> Then again, I don't think a variant type is the answer.  We have std.boxer.Box, but I think there must be a better solution for variadic functions.  Perhaps some special construct for talking about a function's call stack in a portable way...
> 

Hmmm... TypeInfo(_arguments) and pointer to data (_argptr) is 95% of
what variant type is... So why not to add this lacking 5 percent and get
following:

1. Unified method of calling variadic arguments functions
2. No _arguments and _argptr black magic things
3. No need to use of function boxArray()
4. FASTER implementation as no additional function call is necessary -
(you dont need to call box function as compiler (probably) can do
everything on compile time)
5. No need for box function for function when calling following:
	void func(int a1, float a2, variant a3);

	func(5, 2.5, "text");
  In this point I mean clear syntax - not a speed gain.
6. Powerful functionality to implement e.g. database recordsets

Currently I am trying to implement universal Query object, which will
contain all information about SQL query, but not using strings to
represent that query - just abstract objects. It seems that using
variant type is by a few factors easier, more flexible and faster to
implement than using carefully implemented class hirarchy as in e.g.
Java's Hibernate:
http://fisheye.jboss.org/browse/Hibernate/trunk/Hibernate3/src/org/hibernate/criterion
I will send some code when I finish...

Regards
Marcin Kuszczak
Aarti_pl

January 08, 2007
Daniel Keep wrote:
> Marcin Kuszczak wrote:
>> ohhh.. please... do not make another strange _context object!
>>
>> Personally I think that passing information about variadic arguments of
>> function with _arguments and _argptr is black side of D... To get it work
>> properly IMHO there should be build in variant type (which is really
>> usefull in some cases) and arguments to variadic functions could be sent in
>> standard way:
>>
>> void varfunction(variant[] vals ...); // Same syntax like D typesafe
>> variadic functions.
> 
> I've never liked the "magic" _arguments and _argptr.  I always thought they should be explicitly named.

Amen to that.  _arguments and _argptr are a serious wart on D.

My rant and proposed fix on the topic are here:
http://www.prowiki.org/wiki4d/wiki.cgi?BillBaxter

Search for "varargs" on the page.

--bb