www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Exactly Replicating a Function's Signature?

reply %u <wfunction hotmail.com> writes:
If I have an alias F that represents a function, is there any way for me to
create a function func() whose signature is exactly the same as that of F?

This includes parameter types, return types, and any/all storage modifiers
(e.g. const, lazy, scope, etc.).

Without this capability, it's impossible to perform perfect generic
redirection for functions that pass arguments by reference or in a lazy
fashion, right?
Jan 18 2011
parent reply bearophile <bearophileHUGS lycos.com> writes:
%u:

 If I have an alias F that represents a function, is there any way for me to
 create a function func() whose signature is exactly the same as that of F?

In theory this has to be enough: typeof(F) F2; Bye, bearophile
Jan 19 2011
next sibling parent reply %u <wfunction hotmail.com> writes:
 In theory this has to be enough: typeof(F) F2;

But in practice, I want to change the body of my function, or possibly add new parameters in the beginning or the end. Does this let me do either of these? Thank you!
Jan 19 2011
parent reply Jacob Carlborg <doob me.com> writes:
On 2011-01-19 16:29, Andrej Mitrovic wrote:
 Is this what you're looking for?:

 import std.stdio;
 import std.traits;

 void test(alias F)()
 {
      ReturnType!(F) otherFunc(ParameterTypeTuple!(F))
      {
          typeof(return) result;
          return result;
      }
 }

 double foo(int x, int y)
 {
      double result;
      return x + y * 2.5;
 }

 void main()
 {
      test!foo;
 }

Will that keep: ref, out, inout and so on in the signature ? -- /Jacob Carlborg
Jan 19 2011
parent reply %u <wfunction hotmail.com> writes:
 Is this what you're looking for?:

No. :) (Though you already found this out at the end!) I was looking for some way to keep the storage classes and any other (meta)data about the parameters that may be added in the future to the library. I have a feeling this might be a very big change in how DMD handles parameters, but without this feature, perfect redirection -- which I think is possible in C++0x -- would be impossible in D. My suggestion would be that we treat "ref", "out", and "lazy" as type constructors rather than storage specifiers; that way, we could get rid of the concept of storage classes entirely, and it would unify the concept of references with other languages like C# and C++. How does this idea sound? :) (I think it's easier said than done, though...)
Jan 19 2011
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2011-01-19 18:28, %u wrote:
 Is this what you're looking for?:

No. :) (Though you already found this out at the end!) I was looking for some way to keep the storage classes and any other (meta)data about the parameters that may be added in the future to the library. I have a feeling this might be a very big change in how DMD handles parameters, but without this feature, perfect redirection -- which I think is possible in C++0x -- would be impossible in D.

The ugly solution would be to do this: void test (ref int a) { } void main () { writeln(typeof(&test).stringof); } Which will print: "void function(ref int a)" and then parse out what you need.
 My suggestion would be that we treat "ref", "out", and "lazy" as type
constructors
 rather than storage specifiers; that way, we could get rid of the concept of
 storage classes entirely, and it would unify the concept of references with
other
 languages like C# and C++.

 How does this idea sound? :) (I think it's easier said than done, though...)

-- /Jacob Carlborg
Jan 19 2011
parent reply %u <wfunction hotmail.com> writes:
 The ugly solution would be to do this:
 void test (ref int a) { }
 void main () { writeln(typeof(&test).stringof); }
 Which will print: "void function(ref int a)" and then parse out what you need.

If you're referring to using mixin() to parse the signature, I've already thought of that, but it won't work because if the type of any parameter is a user-defined type, it will be inaccessible outside the module that provides this facility. This feature will require compiler and/or language modification(s); I don't believe you can add the storage-class feature correctly with the current language (though please correct me if I'm wrong).
 Let me see if I get this straight (correct me if I'm wrong in understanding
this).
 So you mean we would have e.g. ref as a type constructor:

Yes.
 The problem with this is that now you're functions look like this:
 foo(int x) { }

No... why? ref would be just like const -- so your parameter's data type could be ref(const(string)). Why do you say the functions remove the type constructor from the header? [This is a bit unrelated, but while we're on the topic of documenting code through ref/out: I think it would be better if the language required the "ref" and "out" keywords on passing the parameters, like C# does... it prevents a lot of bugs that can happen when a function's header is changed, since now the caller code won't compiler, instead of compiling but working potentially incorrectly.) I actually noticed a different problem with treating storage classes as type constructors: while "ref" and "lazy" would work well, "out" would make no sense as a type constructor, so this solution probably won't work at all. Does anyone have any ideas on how to get storage classes to work correctly with ParameterTypeTuple, whether or not it's a dirty solution (so long as the solution works)?
Jan 19 2011
parent %u <wfunction hotmail.com> writes:
 ref(const(immutable(string)[])) ?
 That's crazy!

Did you mean ref(const(immutable(char)[]))?? :] Haha... well it was just an idea on how to add the functionality, and like I mentioned, even if it was fine for 'ref' and 'lazy', it wouldn't make any sense for 'out' anyway, so I'd say screw my idea; forget it. :) The trouble with mixin is that it doesn't work the way templates like ParameterTypeTuple do, because if you dynamically generate the header then one of these must be true: 1. You can mixin() it in a template in Phobos, but that won't work because any data types not in the current (Phobos) module can't be referenced, which basically erases this solution completely. 2. You can just return a string and have the user use mixin() every time, but that's a very ugly and error-prone solution, since it would litter the user's code with lots of mixin()s. Hope that made sense... so my conclusion is that, with the current language/compiler features, it's likely there's no workable solution for this (although I'm happy to be proven wrong!!). :\
Jan 19 2011
prev sibling parent wrzosk <dprogr gmail.com> writes:
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

A little example ...
Jan 19 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Is this what you're looking for?:

import std.stdio;
import std.traits;

void test(alias F)()
{
    ReturnType!(F) otherFunc(ParameterTypeTuple!(F))
    {
        typeof(return) result;
        return result;
    }
}

double foo(int x, int y)
{
    double result;
    return x + y * 2.5;
}

void main()
{
    test!foo;
}
Jan 19 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Sorry, here's a more complete example:

import std.stdio;
import std.traits;

void test(alias F)(int x, int y)
{
    ReturnType!(F) otherFunc(ParameterTypeTuple!(F) args)
    {
        typeof(return) result;

        result = args[0] + args[1] * 2.5;

        return result;
    }

    writeln(otherFunc(x, y));
}

double foo(int x, int y)
{
    double result;
    return x + y * 2.5;
}

void main()
{
    test!(foo)(4, 5);
}
Jan 19 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Better:

void test(alias F)(ParameterTypeTuple!(F) args)
{
    ReturnType!(F) otherFunc(ParameterTypeTuple!(F) args)
    {
        typeof(return) result;

        result = args[0] + args[1] * 2.5;

        return result;
    }

    return otherFunc(args);
}

double foo(int x, int y)
{
    double result;
    return x + y * 2.5;
}

void main()
{
    writeln(test!(foo)(4, 5));
}
Jan 19 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Holy cow is that a bug? It's returning a value even though it's a void function.
Jan 19 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Never mind my stupid build script didn't recompile and it ran an old
version. Fixed:

import std.stdio;
import std.traits;

auto test(alias F)(ParameterTypeTuple!(F) args)
{
    ReturnType!(F) otherFunc(ParameterTypeTuple!(F) args)
    {
        typeof(return) result;

        result = args[0] + args[1] * 2.5;

        return result;
    }

    return otherFunc(args);
}

double foo(int x, int y)
{
    double result;
    return x + y * 2.5;
}

void main()
{
    writeln(test!(foo)(4, 5));
}
Jan 19 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I never thought of that. There's a ParameterStorageClassTuple
template, but I don't see a template that combines both of these
templates to duplicate the exact signature. Something like that should
probably be added to Phobos.
Jan 19 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 1/19/11, %u <wfunction hotmail.com> wrote:
 Is this what you're looking for?:

No. :) (Though you already found this out at the end!) I was looking for some way to keep the storage classes and any other (meta)data about the parameters that may be added in the future to the library. I have a feeling this might be a very big change in how DMD handles parameters, but without this feature, perfect redirection -- which I think is possible in C++0x -- would be impossible in D. My suggestion would be that we treat "ref", "out", and "lazy" as type constructors rather than storage specifiers; that way, we could get rid of the concept of storage classes entirely, and it would unify the concept of references with other languages like C# and C++. How does this idea sound? :) (I think it's easier said than done, though...)

Let me see if I get this straight (correct me if I'm wrong in understanding this). So you mean we would have e.g. ref as a type constructor: ref int x; The problem with this is that now you're functions look like this: foo(int x) { } You can't tell if foo modifies x just by looking at the signature. You would have to look at the calling site to find out which arguments were passed, and then you'd have to look at their declaration. (well, unless you use an IDE of some sort that does this automatically, it's 2011 I guess.. :-) ). Having "in", "ref", "out".. in function parameters helps in understanding your own code. If you look at e.g. Microsoft header files there are a lot of methods named like so: foo(/*in*/ param1, /*in*/ param2, /*out*/ param3) D just took this existing practice and made it a language feature. Anyway, I don't know of a workaround for the full signature problem. For a second I thought about using a string mixin, but mixins can't be used inside the function signatures, so I can't just glue together "ref" and "int".
Jan 19 2011
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 1/20/11, %u <wfunction hotmail.com> wrote:
 No... why? ref would be just like const -- so your parameter's data type
 could be
 ref(const(string)).

ref(const(immutable(string)[])) ? That's crazy! Anyway I don't really know what we're talking about anymore. :) s/we're/I'm The string mixin solution provided by wrzosk is similar to what I came up with. Maybe someone who wrote those templates can help out.
Jan 19 2011