Thread overview
What's wrong with this template function?
Jan 03, 2019
Machine Code
Jan 03, 2019
Ali Çehreli
Jan 03, 2019
Machine Code
Jan 03, 2019
Neia Neutuladh
Jan 04, 2019
Machine Code
January 03, 2019
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));
}
January 03, 2019
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

January 03, 2019
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]);
}

January 03, 2019
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.
January 04, 2019
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 :)