Jump to page: 1 2 3
Thread overview
Why do meta programming with templates?
Jan 24, 2006
BCS
Jan 24, 2006
James Dunne
Jan 24, 2006
Sean Kelly
Jan 24, 2006
BCS
Jan 24, 2006
Sean Kelly
Jan 24, 2006
Tom S
Jan 24, 2006
James Dunne
Jan 25, 2006
Tom S
Jan 25, 2006
James Dunne
Jan 25, 2006
David Medlock
Jan 24, 2006
Sean Kelly
Jan 24, 2006
Don Clugston
Jan 24, 2006
xs0
Jan 24, 2006
Don Clugston
Jan 24, 2006
Agent Orange
Jan 24, 2006
Don Clugston
Jan 24, 2006
xs0
Jan 24, 2006
Don Clugston
Jan 24, 2006
xs0
Jan 24, 2006
pragma
Jan 24, 2006
Craig Black
Jan 24, 2006
Sean Kelly
Jan 24, 2006
Sean Kelly
January 24, 2006
Why is meta programming being done with templates? I have seen a number of vary nice things done with that technique but it seems to me to be a terribly convoluted way to go about it. Why not place some of the functionality right in the language it's self?

One construct that would be vary useful is described below:

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

WitheachStatement:
	witheach (WitheachTypeList; AggregateTypeExpression) Statement

WitheachTypeList:
	AliasIdentifier

AliasIdentifier
	inout alias Identifier
	inout alias Identifier

AggregateTypeExpression
	classExpression
	structExpression

Statement is parsed once for each member of AggregateTypeExpression with AliasIdentifier defined as an alias to the current member. For all members, Statement must be semantically ccorrect. static if statements inside of Statement are evaluated per-member. Different members of AggregateTypeExpression can be of different types and need not be converted to a common type.

<code>
	struct A
	{
		int j;
		char c;
		double f;
	}

	A a;

	char[] ret = "";

	witheach(alias i; a)
	{
		static if(is(c:char))
		{
			ret ~= i;
		}
		else
		{
			ret += toString(i);
		}
	}
</code>

is equivalent to:

<code>
	struct A
	{
		int j;
		char c;
		double f;
	}

	A a;

	char[] ret = "";

		ret ~=	toString(a.j);
		ret ~=	char a.c;
		ret ~=	toString(a.f);
</code>


Uses:

With a set functions for the native types that converts data between native and big enden encoding, a simple function can be written that will convert all of the members of a structure from big enden to native encoding. The advantage to this is that when a new member is added the function will automatically convert it also.

PortableDataStructure ConBigEnden(PortableDataStructure in)
{
	witheach(inout alias i; in)
	{
		i = ConBigEnden(i);
	}
	return in;
}

p.s.

The subject line of this post is NOT a rhetorical question. If there is a reason to use templates, I would be interested in hearing why. Also the above is just a suggested syntax, so feel free to point out flaw, improvements, etc.
January 24, 2006
BCS wrote:
> Why is meta programming being done with templates? I have seen a number of vary nice things done with that technique but it seems to me to be a terribly convoluted way to go about it. Why not place some of the functionality right in the language it's self?
> 
> One construct that would be vary useful is described below:
> 
> ---------------
> 
> WitheachStatement:
>     witheach (WitheachTypeList; AggregateTypeExpression) Statement
> 
> WitheachTypeList:
>     AliasIdentifier
> 
> AliasIdentifier
>     inout alias Identifier
>     inout alias Identifier
> 
> AggregateTypeExpression
>     classExpression
>     structExpression
> 
> Statement is parsed once for each member of AggregateTypeExpression with AliasIdentifier defined as an alias to the current member. For all members, Statement must be semantically ccorrect. static if statements inside of Statement are evaluated per-member. Different members of AggregateTypeExpression can be of different types and need not be converted to a common type.
> 
> <code>
>     struct A
>     {
>         int j;
>         char c;
>         double f;
>     }
> 
>     A a;
> 
>     char[] ret = "";
> 
>     witheach(alias i; a)
>     {
>         static if(is(c:char))
>         {
>             ret ~= i;
>         }
>         else
>         {
>             ret += toString(i);
>         }
>     }
> </code>
> 
> is equivalent to:
> 
> <code>
>     struct A
>     {
>         int j;
>         char c;
>         double f;
>     }
> 
>     A a;
> 
>     char[] ret = "";
> 
>         ret ~=    toString(a.j);
>         ret ~=    char a.c;
>         ret ~=    toString(a.f);
> </code>
> 
> 
> Uses:
> 
> With a set functions for the native types that converts data between native and big enden encoding, a simple function can be written that will convert all of the members of a structure from big enden to native encoding. The advantage to this is that when a new member is added the function will automatically convert it also.
> 
> PortableDataStructure ConBigEnden(PortableDataStructure in)
> {
>     witheach(inout alias i; in)
>     {
>         i = ConBigEnden(i);
>     }
>     return in;
> }
> 
> p.s.
> 
> The subject line of this post is NOT a rhetorical question. If there is a reason to use templates, I would be interested in hearing why. Also the above is just a suggested syntax, so feel free to point out flaw, improvements, etc.

Compile-time code generation!  Another one of those great features that I've been kicking around for my own little language.  This gives me a few ideas for syntax.  I like your thinking!  Keep it up.

Regards,
James Dunne
January 24, 2006
James Dunne wrote:
> 
> Compile-time code generation!  Another one of those great features that I've been kicking around for my own little language.  This gives me a few ideas for syntax.  I like your thinking!  Keep it up.

It certainly beats the heck out of merely hoping that the compiler will inline recursive function calls, which is how this is normally done. But it would need to be a bit more flexible to be ideal.  For example, I'd like to be able to iterate across typelists this way, to automate factory class generation.


Sean
January 24, 2006
BCS wrote:
> Why is meta programming being done with templates? I have seen a number of vary nice things done with that technique but it seems to me to be a terribly convoluted way to go about it. Why not place some of the functionality right in the language it's self?

I think there are so many meta programming possibilities that an enormous number of features would need to be added to the language.
But it's definitely worth considering which things are currently convoluted, and whether simplifications are possible.

The example you provide is currently not possible at all with meta programming:

> WitheachStatement:
>     witheach (WitheachTypeList; AggregateTypeExpression) Statement

> AggregateTypeExpression
>     classExpression
>     structExpression

> <code>
>     struct A
>     {
>         int j;
>         char c;
>         double f;
>     }
> 
>     A a;
> 
>     char[] ret = "";
> 
>     witheach(alias i; a)
>     {
>         static if(is(c:char))
>         {
>             ret ~= i;
>         }
>         else
>         {
>             ret += toString(i);
>         }
>     }
> </code>

I think this actually an example of two things:
(a) compile-time reflection.
(b) compile-time for loops.

Currently there's no way to determine the members of a class, struct, module or function at compile-time. Definitely, some form of compile-time reflection would be very cool.
A limitation of this witheach() syntax is that (for example) it doesn't give you any way of determining at compile time how many members there are in the struct. There's no such thing as a compile-time variable, for example, so looping constructs won't work very well without some major additions to the language.


Here's a first pass at how I'd do this with metaprogramming:
------
template makeString(X, int n=0)
{
  char [] makeString(X x) {
    static if (X.membercount == n)
        return "";
    else static if (X.memberof[n].membercount>0)
        // deal with case where a member is itself a struct
        return makeString!( X.memberof[n] )(x.memberof[n]) ~ makeString!(X, n+1)(x);
    else static if ( is (X.memberof[n] : char) )
	return x.memberof[n] ~ makeString!(X, n+1);
    else
        return toString(x.memberof[n]) ~ makeString!(X, n+1)(x);
  }
}

A a;
char[] ret = "" ~ makeString!(X)(x);
----------
I don't think that's too terrible, considering that it copes with the
nested struct situation.

For this to work, every expression T would need several new properties:

.membercount

which gives the number of members it has (0 = it is a fundamental type)

.memberof[int n]

which gives the nth member. If T is a type, gives that type; if it's an instance, gives a reference to that member. (This might need to be split into two properties; I'm not sure).
And for completeness:

.parentof

same, except gives the class/module it's nested in. void if no parent (ie, it is a package).

I think that these three properties would give D everything at compile time, that .NET reflection offers at runtime! (When combined with DDL, for run-time linking). And reflection is much more powerful at compile time than run time.

Hmm, might work this into a compile-time reflection proposal.


January 24, 2006
> For this to work, every expression T would need several new properties:
> 
> ..membercount
> 
> which gives the number of members it has (0 = it is a fundamental type)
> 
> ..memberof[int n]
> 
> which gives the nth member. If T is a type, gives that type; if it's an instance, gives a reference to that member. (This might need to be split into two properties; I'm not sure).
> And for completeness:
> 
> ..parentof
> 
> same, except gives the class/module it's nested in. void if no parent (ie, it is a package).
> 
> I think that these three properties would give D everything at compile time, that .NET reflection offers at runtime! (When combined with DDL, for run-time linking). And reflection is much more powerful at compile time than run time.
> 
> Hmm, might work this into a compile-time reflection proposal.

don't forget .superof, returning the super class for classes.. Or did I miss it and it already exists?


xs0
January 24, 2006
xs0 wrote:
>> For this to work, every expression T would need several new properties:
>>
>> ..membercount
>>
>> which gives the number of members it has (0 = it is a fundamental type)
>>
>> ..memberof[int n]
>>
>> which gives the nth member. If T is a type, gives that type; if it's an instance, gives a reference to that member. (This might need to be split into two properties; I'm not sure).
>> And for completeness:
>>
>> ..parentof
>>
>> same, except gives the class/module it's nested in. void if no parent (ie, it is a package).
>>
>> I think that these three properties would give D everything at compile time, that .NET reflection offers at runtime! (When combined with DDL, for run-time linking). And reflection is much more powerful at compile time than run time.
>>
>> Hmm, might work this into a compile-time reflection proposal.
> 
> 
> don't forget .superof, returning the super class for classes.. Or did I miss it and it already exists?

You're right.
Aargh, there's interfaces too, which can be variable in number, just like members.

Right now, because there's no implicit template instantiation, we can't do function parameters properly, but we can get close. We can do all built-in types, and for classes, structs and typedefs, we can get their name as a const char [] text string (just not as a type).

Similarly, for .parentof, in most cases we can already get the name of the parent as a const char [], but that is no use for wandering the syntax trees at compile time.
January 24, 2006
In article <dr53e6$1mpu$1@digitaldaemon.com>, Don Clugston says...
>
>xs0 wrote:
>>> For this to work, every expression T would need several new properties:
>>>
>>> ..membercount
>>>
>>> which gives the number of members it has (0 = it is a fundamental type)
>>>
>>> ..memberof[int n]
>>>
>>> which gives the nth member. If T is a type, gives that type; if it's
>>> an instance, gives a reference to that member. (This might need to be
>>> split into two properties; I'm not sure).
>>> And for completeness:
>>>
>>> ..parentof
>>>
>>> same, except gives the class/module it's nested in. void if no parent (ie, it is a package).
>>>
>>> I think that these three properties would give D everything at compile time, that .NET reflection offers at runtime! (When combined with DDL, for run-time linking). And reflection is much more powerful at compile time than run time.
>>>
>>> Hmm, might work this into a compile-time reflection proposal.
>> 
>> 
>> don't forget .superof, returning the super class for classes.. Or did I miss it and it already exists?
>
>You're right.
>Aargh, there's interfaces too, which can be variable in number, just
>like members.
>
>Right now, because there's no implicit template instantiation, we can't do function parameters properly, but we can get close. We can do all built-in types, and for classes, structs and typedefs, we can get their name as a const char [] text string (just not as a type).
>
>Similarly, for .parentof, in most cases we can already get the name of the parent as a const char [], but that is no use for wandering the syntax trees at compile time.

typeof(super)  ?






January 24, 2006
Agent Orange wrote:
> In article <dr53e6$1mpu$1@digitaldaemon.com>, Don Clugston says...
> 
>>xs0 wrote:
>>
>>>>For this to work, every expression T would need several new properties:
>>>>
>>>>..membercount
>>>>
>>>>which gives the number of members it has (0 = it is a fundamental type)
>>>>
>>>>..memberof[int n]
>>>>
>>>>which gives the nth member. If T is a type, gives that type; if it's an instance, gives a reference to that member. (This might need to be split into two properties; I'm not sure).
>>>>And for completeness:
>>>>
>>>>..parentof
>>>>
>>>>same, except gives the class/module it's nested in. void if no parent (ie, it is a package).
>>>>
>>>>I think that these three properties would give D everything at compile time, that .NET reflection offers at runtime! (When combined with DDL, for run-time linking). And reflection is much more powerful at compile time than run time.
>>>>
>>>>Hmm, might work this into a compile-time reflection proposal.
>>>
>>>
>>>don't forget .superof, returning the super class for classes.. Or did I miss it and it already exists?
>>
>>You're right.
>>Aargh, there's interfaces too, which can be variable in number, just like members.
>>
>>Right now, because there's no implicit template instantiation, we can't do function parameters properly, but we can get close. We can do all built-in types, and for classes, structs and typedefs, we can get their name as a const char [] text string (just not as a type).
>>
>>Similarly, for .parentof, in most cases we can already get the name of the parent as a const char [], but that is no use for wandering the syntax trees at compile time.
> 
> 
> typeof(super)  ?

Ah!
That almost works. You just need to add:

   alias typeof(super) superof;

to the definition of every class.
It does not seem possible to obtain it otherwise. You cannot use a dot syntax, for example. (can't even say this.super), so unfortunately you cannot say super.super with any arrangement of typeof() that I can come up with.
January 24, 2006
>> typeof(super)  ?
> 
> 
> Ah!
> That almost works. You just need to add:
> 
>    alias typeof(super) superof;
> 
> to the definition of every class.
> It does not seem possible to obtain it otherwise. You cannot use a dot syntax, for example. (can't even say this.super), so unfortunately you cannot say super.super with any arrangement of typeof() that I can come up with.

Well, besides RTTI (CTTI? :), I was hoping to be able to do this:

class List(T) : List!(T.superof)
{
}

with the idea that a List of Whatevers is also a List of Objects, but is not assignable as such. typeof(super) wouldn't work, I guess, because I want T's super, not List's, and super is probably defined only inside the class anyway. Even if it did work, the compiler would need to specifically handle this idiom, as the above code contains an infinite loop:

T->...->Object->void->void->void...

Come to think of it, would it be awfully weird, if the class hierarchy could be defined conditionally from within the class, not only where it's declared? Something like:

class List(T)
{
    static if (is(T:Object) && !is(T==Object)) {
        this class : List!(T.superof);
    }
}

or

class Foo(T) {
    static if (is(T:Serializable)) {
        this class : Serializable;
    } else {
        this class : PartiallySerializable;
    }
}

With the meaning that Foo!(T) is only serializable, if T is.. It would also allow backward compatible refactoring of class hierarchies, as an older version of the app wouldn't even need to know the class has moved, for example..


xs0
January 24, 2006
xs0 wrote:
> 
>>> typeof(super)  ?
>>
>>
>>
>> Ah!
>> That almost works. You just need to add:
>>
>>    alias typeof(super) superof;
>>
>> to the definition of every class.
>> It does not seem possible to obtain it otherwise. You cannot use a dot syntax, for example. (can't even say this.super), so unfortunately you cannot say super.super with any arrangement of typeof() that I can come up with.
> 
> 
> Well, besides RTTI (CTTI? :), I was hoping to be able to do this:
> 
> class List(T) : List!(T.superof)
> {
> }
> 
> with the idea that a List of Whatevers is also a List of Objects, but is not assignable as such. typeof(super) wouldn't work, I guess, because I want T's super, not List's, and super is probably defined only inside the class anyway. Even if it did work, the compiler would need to specifically handle this idiom, as the above code contains an infinite loop:
> 
> T->...->Object->void->void->void...

void.superof would have to be illegal. In fact, I think .superof would probably be illegal for anything that is not a class/struct.

> 
> Come to think of it, would it be awfully weird, if the class hierarchy could be defined conditionally from within the class, not only where it's declared? Something like:
> 
> class List(T)
> {
>     static if (is(T:Object) && !is(T==Object)) {
>         this class : List!(T.superof);
>     }
> }

Providing you put the superof alias in your classes, you can already do (since it's legal to derive from Object):

template ListBaseClass(T)
{
  static if( is(T: Object) && !is(T==Object))
          alias T.superof ListBaseClass;
  else alias Object ListBaseClass;
}

template List(T)
{
  class List : ListBaseClass!(T) {
   ....
  }
}

> 
> or
> 
> class Foo(T) {
>     static if (is(T:Serializable)) {
>         this class : Serializable;
>     } else {
>         this class : PartiallySerializable;
>     }
> }

template FooBaseClass(T)
{
  static if( is(T: Serializable))
          alias Serializable FooBaseClass;
  else alias PartiallySerializable FooBaseClass;
}

class Foo(T) : public FooBaseClass!(T)
{
  ...
}

> With the meaning that Foo!(T) is only serializable, if T is.. It would also allow backward compatible refactoring of class hierarchies, as an older version of the app wouldn't even need to know the class has moved, for example..
> 
> 
> xs0
« First   ‹ Prev
1 2 3