www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is there a template syntax for alias-of-member?

reply Russell Lewis <webmaster villagersonline.com> writes:
Here's what I'm trying to do.  I want to build a template which takes, 
as its first parameter, a type, and then takes one or more aliases to 
member names.  In other words, the user of the template would write 
something like this:

BEGIN CODE (that doesn't work)
	struct s
	{
		int x;
	}
	void main()
	{
		magic_template!(s, x);
	}
END CODE

However, I can't figure out how to do it.  Right now, I'm stumbling 
along with string mixins, as follows:

BEGIN CODE
	template foo(T,string member)
	{
	  T *of()
	  {
	    T *ret = new T;
	    mixin("ret." ~member~ "= 1;");
	    return ret;
	  }
	}

	struct s
	{
	  int x;
	}

	import std.stdio;
	void main()
	{
	  auto tmp = foo!(s,"x").of();
	  writefln(tmp.x);
	}
END CODE


Does anybody know of a more elegant way to do this?
Mar 07 2008
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Russell Lewis wrote:
 Here's what I'm trying to do.  I want to build a template which takes, 
 as its first parameter, a type, and then takes one or more aliases to 
 member names.  In other words, the user of the template would write 
 something like this:
 
 BEGIN CODE (that doesn't work)
     struct s
     {
         int x;
     }
     void main()
     {
         magic_template!(s, x);
     }
 END CODE
 
 However, I can't figure out how to do it.  Right now, I'm stumbling 
 along with string mixins, as follows:
 
 BEGIN CODE
     template foo(T,string member)
     {
       T *of()
       {
         T *ret = new T;
         mixin("ret." ~member~ "= 1;");
         return ret;
       }
     }
 
     struct s
     {
       int x;
     }
 
     import std.stdio;
     void main()
     {
       auto tmp = foo!(s,"x").of();
       writefln(tmp.x);
     }
 END CODE
 
 
 Does anybody know of a more elegant way to do this?

I think that's about as good as it gets right now. You could eliminate the .of part by making foo a function template. You can also do some interesting things by deducing the type from the "x" part: alias typeof(mixin("ret."~member)) X; ... or maybe you need to put the whole alias expression in a string. I'm not quite clear on what can and can't go into a mixin. If that's not enough, try being more specific about what you are actually trying to do, and maybe someone can offer a better solution. --bb
Mar 07 2008
parent reply Russell Lewis <webmaster villagersonline.com> writes:
Bill Baxter wrote:
 If that's not enough, try being more specific about what you are 
 actually trying to do, and maybe someone can offer a better solution.

I'm trying to build a parser library. The syntax I'm experimenting with is: parse(output_type, <member names>...) where the library figures out the type of each member and automatically calls the correct parse() function at the right time. (The order in which you call the various member names determines the order in which the various nonterminals must show up in the file being parsed.) I already have the mechanics of the parser library working, but it's not easy to use. The point of the template is to auto-generate most of the client-side code.
Mar 07 2008
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Russell Lewis wrote:
 Bill Baxter wrote:
 If that's not enough, try being more specific about what you are 
 actually trying to do, and maybe someone can offer a better solution.

I'm trying to build a parser library. The syntax I'm experimenting with is: parse(output_type, <member names>...) where the library figures out the type of each member and automatically calls the correct parse() function at the right time. (The order in which you call the various member names determines the order in which the various nonterminals must show up in the file being parsed.) I already have the mechanics of the parser library working, but it's not easy to use. The point of the template is to auto-generate most of the client-side code.

Ok. So the literal "=1" part in your example would be replaced with a type specific parse function. I think the direction you're going is the best you can hope for right now. It's pretty darn decent, though, if you ask me. --bb
Mar 07 2008
parent Russell Lewis <webmaster villagersonline.com> writes:
Bill Baxter wrote:
 Ok.  So the literal "=1" part in your example would be replaced with a 
 type specific parse function.

It's not relevant to the question, but I am so enthralled with my parser I just have to tell you about it. :) I'm experimenting with a way to be able to parse ambiguous grammars while reducing the amount of copying required. The problem with an ambiguous grammar is that you have a nonterminal of type foo, which has to parse bar and then baz. It parses the bar first, and then tries to parse the baz; but if there are two possible parsings of baz, then you have to copy the foo struct and keep track of both. It's doable when you have pointers, but when you are parsing arrays of elements and you ask yourself "I have 1000 elements so far...can I parse one more?" it gets hard, because you have to copy the entire array to a new location (keeping the old around) just in case. Now imagine that maybe some of the elements of the array are ambiguous, too, just multiplying the combinations... So what I'm experimenting with are producing "infill" delegates. The parse function for a given nonterminal doesn't actually produce the value type which summarizes the information...it instead produces a delegate which, if it is ever called, will fill a type. So no actual parse tree is produced until you get some overall "whole file parsed" delegate. You can then allocate the struct for the root of the parse tree, call the infill, and it builds the tree for you. Arrays work the same way...the infill delegate for 1000-element array just calls the infill delegate for the 999-element array, and then the infill delegate for the last element. The reason that this is nice is that you never copy stuff around...you simply pass around delegates which have the *potential* to build parse trees. Parsings that you explore which later die simply mean that certain trees of delegates become garbage. Right now, I have most of the common-code mechanisms in place, and I'm starting to actually use it to parse real things. Which is why I finally had to address the client-side code...it's quite ugly, even though I've worked hard with templates to automate things. What I don't know, yet, is whether or not I can make it fast enough. I make heavy use of closures and currying (which makes the code more elegant than it would have been otherwise), but I may just be trading one overhead for another. Plus, the heavy cost of function calls may make all of those nested infill delegates impractical. We'll see.
Mar 07 2008