www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 8106] New: func.stringof with default args

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106

           Summary: func.stringof with default args
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: normal
          Priority: P2
         Component: DMD
        AssignedTo: nobody puremagic.com
        ReportedBy: turkeyman gmail.com



func.stringof is the only existing way to deeply introspect function calling
information past it's argument types (I need argument names, default
arguments), so I rely on this very heavily.

There are some annoying cases where it doesn't work as expected though:

struct Colour
{
  ubyte a,r,g,b;

  immutable Colour white = Colour(255,255,255,255);
}

void func(Colour c = Colour.white)

func.stringof == "void func(Colour c = white)"
Error: undefined identifier white

For some reason the scope Colour. was removed from the initialiser.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 16 2012
next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106


Manu <turkeyman gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Severity|normal                      |major



I raised the priority a notch. I have no workaround for this.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 16 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106


Walter Bright <bugzilla digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |bugzilla digitalmars.com



16:46:31 PDT ---
For this program:
=======================
struct Colour
{
  ubyte a,r,g,b;

  immutable Colour white = Colour(255,255,255,255);
}

void func(Colour c = Colour.white);

void main()
{
   pragma(msg, func.stringof);
}
=======================
it prints:

  func(white)

not:

  void func(Colour c = white)

Can you please verify the behavior you are seeing?

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jun 12 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106





 Can you please verify the behavior you are seeing?
Sorry, that should have a typeof(): pragma(msg, typeof(func).stringof); -> void(Colour c = cast(Colour)white) I don't recall it showing the cast that way at work (old GDC differences perhaps), but that's what I'm seeing right now. Either way, that should surely read c = Colour.white instead. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jun 12 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106




14:55:42 PDT ---
I have a patch for this specific case, but it's a kludge, not a general
solution.

The problem is that the context of the typeof(x) is not the context of where x
was declared, hence it is unknown what scope qualifiers (such as Colour) need
to be applied.

Can you post an example of why you need this? Perhaps there's another way.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jun 13 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106





 Can you post an example of why you need this? Perhaps there's another way.
It's difficult for me to extract a concrete example (a very large context, in work code), but essentially I'm creating functions/methods that match (wrap) user declared function pointers, which include default args in the declarations. The default args need to propagate to the magic wrappers. There is no mechanism to copy a parameter list from one function type to another (with names + default args), nor are there traits to get the argument names, or the default arg expression, so I can only construct declarations for mixins by parsing that string. An alternative approach I can use (prefer) is to invert the problem, allowing the user to define function prototypes (rather than function pointers), and then procedurally produce the function body. Although this conflicts with the limitation I posted in bug 8108, where you can't forward declare/prototype a function in a module/class, and then define it later in the same scope. The prototype would be declared by a user, and a later mixin would scan for prototypes and produce the function bodies within the same module. // user declares: void Function(int x = 10); // note: default args are still important // and using magic, mixin: void Function(int x) { // generated body.. } -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jun 13 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106




20:05:42 PDT ---
Ok, I suggest abandoning this .stringof proposal as unworkable. Instead, how
about a traits that gives the default arguments as an expression tuple?

Also, the argument names shouldn't be necessary, you can just generate your
own.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jun 13 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106




20:50:27 PDT ---
Suppose I made this work:
-------------------------
template ParameterTypeTuple(alias foo)
{
    static if (is(typeof(foo) P == function))
        alias P ParameterTypeTuple;
    else
        static assert(0, "argument has no parameters");
}

int func(int i, long j = 7) { return 3; }

alias ParameterTypeTuple!func PT;

int bar(PT) { return 4; }

pragma(msg, typeof(bar));

void main()
{
    bar(1);
}
----------------------------

I.e. the default argument is propagated to the tuple. Will that work for you?

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jun 13 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106




22:23:21 PDT ---
Note that the default argument can be retrieved using something like:

PT[1] getDefault(PT a) { return a[1]; }

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jun 13 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106




22:35:21 PDT ---
Or better:

PT[1] getDefault(PT[1..2] a) { return a[0]; }

An example:
-----------------
import std.stdio;

template ParameterTypeTuple(alias foo)
{
    static if (is(typeof(foo) P == function))
        alias P ParameterTypeTuple;
    else
        static assert(0, "argument has no parameters");
}

int func(int i, long j = 7) { return 3; }

alias ParameterTypeTuple!func PT;

int bar(PT) { return 4; }

pragma(msg, typeof(bar));
pragma(msg, PT[1]);

PT[1] boo(PT[1..2] a) { return a[0]; }

void main()
{
    writeln(boo());
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jun 13 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106


Walter Bright <bugzilla digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|                            |WONTFIX



22:35:54 PDT ---
I think that's a better solution, so I'm marking this as "won't fix".

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jun 13 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106





 Suppose I made this work:
 -------------------------
 template ParameterTypeTuple(alias foo)

 I.e. the default argument is propagated to the tuple. Will that work for you?
That would be amazing, though the argument names are equally important too. I'm pretty sure someone already had a pull request somewhere to add a __traits for the arg names.
 Or better:
 
 PT[1] getDefault(PT[1..2] a) { return a[0]; }
 
 pragma(msg, typeof(bar));
 pragma(msg, PT[1]);
 
 PT[1] boo(PT[1..2] a) { return a[0]; }
It's not clear to me what those pragma's would produce. Just to reiterate, type, name and default args are all important details about the parameter list. I can't do without all 3. If I'm generating functions to wrap other functions, the new function has to have matching parameter names. If I use nonsense names, then auto-complete pop-ups will just show nonsense to the user, and the user wouldn't know what you're supposed to pass to the function.
 I think that's a better solution, so I'm marking this as "won't fix".
I agree, although perhaps you shouldn't mark this as won't-fix, since I think it's still somewhat of a bug. Just drop the priority? func.stringof ideally shouldn't produce a syntactically incorrect string regardless. Fortunately, with your proposed changes, I won't depend on it. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jun 14 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106




07:14:50 PDT ---


 I.e. the default argument is propagated to the tuple. Will that work for you?
That would be amazing, though the argument names are equally important too. I'm pretty sure someone already had a pull request somewhere to add a __traits for the arg names.
I looked, but didn't see it.
 pragma(msg, typeof(bar));
 pragma(msg, PT[1]);
It's not clear to me what those pragma's would produce.
They're just informative.
 If I'm generating functions to wrap other functions, the new function has to
 have matching parameter names. If I use nonsense names, then auto-complete
 pop-ups will just show nonsense to the user, and the user wouldn't know what
 you're supposed to pass to the function.
Ok, so it's a user-experience issue, not one where it does not work. Let me think about it. It's important that I understand what issue you're trying to solve.
 

 I think that's a better solution, so I'm marking this as "won't fix".
I agree, although perhaps you shouldn't mark this as won't-fix, since I think it's still somewhat of a bug. Just drop the priority? func.stringof ideally shouldn't produce a syntactically incorrect string regardless.
It doesn't produce a syntactically incorrect string, it produces one that semantically won't compile in the context it is in. I reiterate that the problem is which names are in scope and which aren't? It's just an impractical problem, and making it work in one case will break others. It's a game of whack-a-mole. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jun 14 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106







 pragma(msg, typeof(bar));
 pragma(msg, PT[1]);
It's not clear to me what those pragma's would produce.
They're just informative.
What I mean is, I don't quite follow the syntax of your proposal. What would be the output of the pragmas you illustrate? Ie, what _exactly_ does PT contain? I just understand you intend that it will somehow contain the type AND default arg expression (but not the name?), rather than just the type as it does now... how does that work in practise syntactically?
 If I'm generating functions to wrap other functions, the new function has to
 have matching parameter names. If I use nonsense names, then auto-complete
 pop-ups will just show nonsense to the user, and the user wouldn't know what
 you're supposed to pass to the function.
Ok, so it's a user-experience issue, not one where it does not work. Let me think about it. It's important that I understand what issue you're trying to solve.
In this case, only that aspect (the names) is a UX issue, the rest is mechanical. The default args are required in the wrapper I generate or calling code won't compile. That said, I wouldn't be satisfied with the solution if I didn't have access to the names, and I would have to persist with tedious parsing FuncType.stringof to gather them again... There are other cases where I have needed the names too. In mixins inserted into a function body; without a trait that can give the argument names, there's no way to refer to function arguments from within the mixin. I have run in to this situations numerous times, but I've worked around them at the cost of code complexity since there was no solution at the time. I'm just saying, access to the parameter names is also important if you're going to the effort of being able to introspect the default arg expressions.
 It doesn't produce a syntactically incorrect string, it produces one that
 semantically won't compile in the context it is in. I reiterate that the
 problem is which names are in scope and which aren't? It's just an impractical
 problem, and making it work in one case will break others. It's a game of
 whack-a-mole.
I'd imagine that what should actually be expected, is the expression relative to the scope where it was declared. Ie, the expression as it was written in the declaration. Nothing else makes any sense by my logic. But this is obviously not particularly important. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jun 14 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106




---
Now, this small utility works as expected.

/*
 */
template ParameterDefaultValueTuple(alias func)
{
    alias ParameterTypeTuple!func PT;

    template DefArg(size_t i)
    {
        enum get = (PT[i..i+1] args) => args[0];
        static if (is(typeof(get())))
            enum DefArg = get();
        else
            alias void DefArg;
            // If default arg doesn't exist, returns void instead.
    }
    template Impl(size_t i = 0)
    {
        static if (i == PT.length)
            alias TypeTuple!() Impl;
        else
            alias TypeTuple!(DefArg!(i), Impl!(i+1)) Impl;
    }

    alias Impl!() ParameterDefaultValueTuple;
}

unittest
{
    void foo(int n = 1, string s = "hello"){}
    //pragma(msg, ParameterDefaultValueTuple!foo);
    static assert(ParameterDefaultValueTuple!foo.length == 2);
    static assert(ParameterDefaultValueTuple!foo[0] == 1);
    static assert(ParameterDefaultValueTuple!foo[1] == "hello");
    static assert(is(typeof(ParameterDefaultValueTuple!foo) ==
                     typeof(TypeTuple!(1, "hello"))));

    void bar(int x, int n = 1, string s = "hello"){}
    //pragma(msg, ParameterDefaultValueTuple!bar);
    static assert(ParameterDefaultValueTuple!bar.length == 3);
    static assert(is(ParameterDefaultValueTuple!bar[0] == void));
    static assert(   ParameterDefaultValueTuple!bar[1] == 1);
    static assert(   ParameterDefaultValueTuple!bar[2] == "hello");
    static assert(is(typeof(ParameterDefaultValueTuple!bar) ==
                     typeof(TypeTuple!(void, 1, "hello"))));

    struct Colour
    {
      ubyte a,r,g,b;

      immutable Colour white = Colour(255,255,255,255);
    }
    void bug8106(Colour c = Colour.white){}
    //pragma(msg, ParameterDefaultValueTuple!bug8106);
    static assert(ParameterDefaultValueTuple!bug8106[0] == Colour.white);
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jun 14 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106




19:32:45 PDT ---
How about:

1. reverting the

    is(typeof(foo) P == function)

to the old 2.059 behavior of being a type tuple only.

2. adding a "parameters" to traits, so that:

    __traits(parameters, foo)

returns a tuple that includes types, identifiers, and default values (like the
2.060 beta does now)?

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jun 25 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106




---

 How about:
 
 1. reverting the
 
     is(typeof(foo) P == function)
 
 to the old 2.059 behavior of being a type tuple only.
I think it is necessary.
 2. adding a "parameters" to traits, so that:
 
     __traits(parameters, foo)
 
 returns a tuple that includes types, identifiers, and default values (like the
 2.060 beta does now)?
That is the most handy way now we can do. And, to wrap the feature, we can add utility templates to std.trait. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jun 25 2012
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8106





 2. adding a "parameters" to traits, so that:
 
     __traits(parameters, foo)
 
 returns a tuple that includes types, identifiers, and default values (like the
 2.060 beta does now)?
Will that also include the storage class: const/ref/in/out/scope/...? How would the code look to mirror an argument list to a new function? And how would the code look to pick and choose individual pieces from the parameter definition (ie, to capture just the name)? I suppose std.traits would gain a bunch of helpers? -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jun 26 2012