www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - What's wrong with this template function?

reply Machine Code <jckj33 gmail.com> writes:
I wrote a small routine to return the first member of type T of a 
same type, like struct below, but the assert is reached albeit 
the "yes" message is printed. What am I missing? should I use 
something else than return keyword to return from a template 
function or what?

struct Color
{
	enum red = Color("red", 30);
	enum blue = Color("blue", 40);
	enum green = Color("green");

	string name;
	int value;
	alias value this;
}

the routine:

T first(T)()
{
	import std.string : format;
	pragma(msg, format!"types = %s"([__traits(derivedMembers, T)]));
	
	static foreach(field; [__traits(derivedMembers, T)])
	{
		// exit on first match
		pragma(msg, format!"member %s"(field));
		static if(is(typeof(__traits(getMember, T, field)) == T))
		{
			pragma(msg, "yes");
			return __traits(getMember, T, field);
		}
		else
		{
			pragma(msg, "no");
		}
	}
	import std.string : format;
	static assert(0,
		format!"no first member of type %s found"(T.stringof));
}
Jan 03
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 01/03/2019 10:49 AM, Machine Code wrote:

 I wrote a small routine to return the first member
I see that that's possible because the values of such members are known at compile time in your case. Otherwise, you would need a mechanism that would return the value of the first member for any object at runtime.
 of type T of a same
 type, like struct below, but the assert is reached albeit the "yes"
 message is printed. What am I missing? should I use something else than
 return keyword to return from a template function or what?

 struct Color
 {
      enum red = Color("red", 30);
      enum blue = Color("blue", 40);
      enum green = Color("green");

      string name;
      int value;
      alias value this;
 }

 the routine:

 T first(T)()
 {
      import std.string : format;
      pragma(msg, format!"types = %s"([__traits(derivedMembers, T)]));

      static foreach(field; [__traits(derivedMembers, T)])
      {
          // exit on first match
          pragma(msg, format!"member %s"(field));
          static if(is(typeof(__traits(getMember, T, field)) == T))
          {
              pragma(msg, "yes");
              return __traits(getMember, T, field);
          }
          else
          {
              pragma(msg, "no");
          }
      }
      import std.string : format;
      static assert(0,
          format!"no first member of type %s found"(T.stringof));
That will always be checked at compile time and will always fail because that line is not excluded from the compilation by another compile-time check. It is a part of the function body and the compiler will have to compile it and fail that check.
 }
You're basically performing a search at compile time and want to fail if something is not found. I came up with the following method where a nested function is used to return an index. The outer code calls the function to set a compile-time expression (found) and is able to check that something is found. (There are too many size_t.max's in the code; cleanup needed. :) ) struct Color { enum red = Color("red", 30); enum blue = Color("blue", 40); enum green = Color("green"); string name; int value; alias value this; } T first(T)() { import std.string : format; pragma(msg, format!"types = %s"([__traits(derivedMembers, T)])); alias fields = __traits(derivedMembers, T); auto first_() { auto result = size_t.max; static foreach(i, field; fields) { // exit on first match pragma(msg, format!"member %s"(field)); static if(is(typeof(__traits(getMember, T, field)) == T)) { pragma(msg,"yes"); if (result == size_t.max) { result = i; } } else { pragma(msg, "no"); } } return result; } enum found = first_(); import std.string : format; static assert(found != size_t.max, format!"no first member of type %s found"(T.stringof)); return __traits(getMember, T, fields[found]); } void main() { pragma(msg, first!Color); } Ali
Jan 03
parent reply Machine Code <jckj33 gmail.com> writes:
On Thursday, 3 January 2019 at 19:38:39 UTC, Ali Çehreli wrote:
 On 01/03/2019 10:49 AM, Machine Code wrote:

 I wrote a small routine to return the first member
I see that that's possible because the values of such members are known at compile time in your case. Otherwise, you would need a mechanism that would return the value of the first member for any object at runtime.
 of type T of a same
 type, like struct below, but the assert is reached albeit the
"yes"
 message is printed. What am I missing? should I use something
else than
 return keyword to return from a template function or what?

 struct Color
 {
      enum red = Color("red", 30);
      enum blue = Color("blue", 40);
      enum green = Color("green");

      string name;
      int value;
      alias value this;
 }

 the routine:

 T first(T)()
 {
      import std.string : format;
      pragma(msg, format!"types =
%s"([__traits(derivedMembers, T)]));
      static foreach(field; [__traits(derivedMembers, T)])
      {
          // exit on first match
          pragma(msg, format!"member %s"(field));
          static if(is(typeof(__traits(getMember, T, field))
== T))
          {
              pragma(msg, "yes");
              return __traits(getMember, T, field);
          }
          else
          {
              pragma(msg, "no");
          }
      }
      import std.string : format;
      static assert(0,
          format!"no first member of type %s
found"(T.stringof)); That will always be checked at compile time and will always fail because that line is not excluded from the compilation by another compile-time check. It is a part of the function body and the compiler will have to compile it and fail that check.
 }
You're basically performing a search at compile time and want to fail if something is not found. I came up with the following method where a nested function is used to return an index. The outer code calls the function to set a compile-time expression (found) and is able to check that something is found. (There are too many size_t.max's in the code; cleanup needed. :) ) struct Color { enum red = Color("red", 30); enum blue = Color("blue", 40); enum green = Color("green"); string name; int value; alias value this; } T first(T)() { import std.string : format; pragma(msg, format!"types = %s"([__traits(derivedMembers, T)])); alias fields = __traits(derivedMembers, T); auto first_() { auto result = size_t.max; static foreach(i, field; fields) { // exit on first match pragma(msg, format!"member %s"(field)); static if(is(typeof(__traits(getMember, T, field)) == T)) { pragma(msg,"yes"); if (result == size_t.max) { result = i; } } else { pragma(msg, "no"); } } return result; } enum found = first_(); import std.string : format; static assert(found != size_t.max, format!"no first member of type %s found"(T.stringof)); return __traits(getMember, T, fields[found]); } void main() { pragma(msg, first!Color); } Ali
Thank you very much, Ali. So the issue was basically I can't return from a static foreach() loop right? I think I've read that but totally forgot. I just used -1 instead of size_t.max and turned into array the result from __traits(derivedMembers, T) so that I index it later on, in case of the function return. I did small changes, end up with this: T first(T)() { enum fields = [__traits(derivedMembers, T)]; auto first_() { auto result = -1; static foreach(i, field; fields) { static if(is(typeof(__traits(getMember, T, field)) == T)) { if(result == -1) { result = i; } } } return result; } enum found = first_(); import std.string : format; static assert(found != -1, format!"no first member of type %s found"(T.stringof)); return __traits(getMember, T, fields[found]); }
Jan 03
parent reply Neia Neutuladh <neia ikeran.org> writes:
On Thu, 03 Jan 2019 20:34:17 +0000, Machine Code wrote:
 Thank you very much, Ali. So the issue was basically I can't return from
 a static foreach() loop right?
The static foreach is done at compile time and the return is done at runtime. After the template is expanded, your code ends up looking like: Color first() { return Color.red; return Color.blue; return Color.green; static assert(false); } And that doesn't compile, because there's a static assert there that fails. It's not at any line of code that would execute at runtime, but the compiler doesn't care about that.
Jan 03
parent Machine Code <jckj33 gmail.com> writes:
On Thursday, 3 January 2019 at 21:41:44 UTC, Neia Neutuladh wrote:
 On Thu, 03 Jan 2019 20:34:17 +0000, Machine Code wrote:
 Thank you very much, Ali. So the issue was basically I can't 
 return from a static foreach() loop right?
The static foreach is done at compile time and the return is done at runtime. After the template is expanded, your code ends up looking like: Color first() { return Color.red; return Color.blue; return Color.green; static assert(false); } And that doesn't compile, because there's a static assert there that fails. It's not at any line of code that would execute at runtime, but the compiler doesn't care about that.
Thank you for the clarification :)
Jan 03