www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Template context

reply Daniel Keep <daniel.keep+lists gmail.com> writes:
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...
Jan 07 2007
next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
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
Jan 07 2007
next sibling parent Daniel Keep <daniel.keep+lists gmail.com> writes:
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
Jan 07 2007
prev sibling parent reply Marcin Kuszczak <aarti interia.pl> writes:
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/ -------------------------------------
Jan 07 2007
parent reply Daniel Keep <daniel.keep+lists gmail.com> writes:
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
Jan 08 2007
next sibling parent Aarti_pl <aarti interia.pl> writes:
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
Jan 08 2007
prev sibling next sibling parent Aarti_pl <aarti interia.pl> writes:
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
Jan 08 2007
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
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
Jan 08 2007
prev sibling parent Georg Wrede <georg nospam.org> writes:
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.
Jan 07 2007