Bug 1839 – Give D a modern varargs system that allows forwarding
Status
RESOLVED
Resolution
WONTFIX
Severity
enhancement
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
x86
OS
Windows
Creation time
2008-02-14T21:48:39Z
Last change time
2018-02-24T18:49:45Z
Assigned to
No Owner
Creator
Bill Baxter
Comments
Comment #0 by wbaxter — 2008-02-14T21:48:39Z
Given function
void A(...) {
...
}
It should be possible to write a function wrapsA of this sort:
void wrapsA(...) {
pre_call();
A(...); // or whatever syntax you want for forwarding the varargs.
post_call();
}
That D -- a supposedly modern, easy-to-use languages -- can't do this conceptually simple thing is sad.
The whole point of procedural programming is that code is encapsulated into reusable blocks. Not being able to forward varargs means that a big chunk of that reusability is lost any time you write a varargs function.
The workaround is to write every varargs function in two flavors, one thats got (...) args, and one that's got (_argptr,_arguments) args. But if the solution is so mechanical and straightforward then why can't the compiler at just do that for us? I'm not saying that's the right way to implement vararg forwarding, but barring a more elegant solution, that sounds like at least a plausible solution.
Comment #1 by caron800 — 2008-02-15T01:51:50Z
On 15/02/2008, [email protected] <[email protected]> wrote:
> That D -- a supposedly modern, easy-to-use languages -- can't do this
> conceptually simple thing is sad.
Will this not work?
import std.traits;
ReturnType!(f) wraps(alias f)(ParameterTypeTuple!(f) t)
{
pre_call();
ReturnType!(f) x = f(t);
post_call();
return x;
}
wrap!(A)(params);
Comment #2 by wbaxter — 2008-02-15T01:56:18Z
Sometimes you can't / don't want to use a template.
If templates were an answer then we wouldn't need the regular vararg functions to begin with.
Comment #3 by wbaxter — 2008-02-15T01:58:53Z
...and what is the parameter type tuple of a varargs function?
I suspect that probably doesn't work actually.
Comment #4 by andrei — 2008-02-15T02:36:50Z
"..."-style varargs predate template varargs and have a number of disadvantages compared to them, so it is likely they will get deprecated in favor of templates and macros.
Comment #5 by wbaxter — 2008-02-15T02:39:04Z
If (...) functions go away, then yes, this bug report becomes pointless. The template versions of forwarding will do the trick.
But how do I create a delegate that takes an arbitrary list of arbitrary arguments using templates?
Comment #6 by andrei — 2008-02-15T02:47:06Z
(In reply to comment #5)
> If (...) functions go away, then yes, this bug report becomes pointless. The
> template versions of forwarding will do the trick.
>
> But how do I create a delegate that takes an arbitrary list of arbitrary
> arguments using templates?
>
import std.stdio;
void before() {}
void after() {}
void forward(F, T...)(F fun, T args)
{
before();
scope(exit) after();
fun(args);
}
void main()
{
forward((int a) { writeln(a + 40); }, 2);
}
Comment #7 by wbaxter — 2008-02-15T02:58:39Z
I mean this:
----
void delegate(...) callback = &fn;
...
callback("hi", 10, "mom");
----
Does your snippet of code show how to do that? If so, I'm not seeing the connection, so please explain again.
Comment #8 by andrei — 2008-02-15T03:35:24Z
I misunderstood your initial code sample. In fact that's a simpler problem:
import std.stdio;
void before() {}
void after() {}
void fn(...)
{
foreach (i; 0 .. _arguments.length)
{
_arguments[i].print;
}
if (_arguments[0] == typeid(int))
{
int j = *cast(int *)_argptr;
writeln(j);
}
}
void wrapsFn(T...)(T args)
{
before();
scope(exit) after();
fn(args);
}
void main()
{
wrapsFn(2, 42, "a");
}
Comment #9 by aarti — 2008-02-15T04:16:19Z
Isn't it that variable arguments templates have its own problems also?
Executable size will increase very quickly for different types/combinations of parameters. AFAIK it will happen especially when template body will be very big. Sure you can split big template into smaller, but it means also that size of program will heavily depend on coding style of programmer. Or is it possible to optimize such a big template automagically?
But I think that, if variable argument templates are not able to be instant replacement (considering e.g. size of executable), variable arguments functions should stay...
When they would stay a few simple fixes would probably enhance current situation in order of magnitude.
1. Defining struct, which will keep typeinfo pointer and data pointer
e.g. struct Arg {TypeInfo type; void* ptr;}
2. Then variable argument functions could be declared like other type safe variadic argument functions in D:
void func(Arg[] param...) {}
...and the problem with passing arguments would be solved... (also ugly local variables (=hacks): _argptr, _arguments could disappear).
Comment #10 by wbaxter — 2008-02-15T04:58:06Z
(In reply to comment #8)
> I misunderstood your initial code sample. In fact that's a simpler problem:
>
> import std.stdio;
>
> void before() {}
> void after() {}
>
> void fn(...)
> {
> foreach (i; 0 .. _arguments.length)
> {
> _arguments[i].print;
> }
> if (_arguments[0] == typeid(int))
> {
> int j = *cast(int *)_argptr;
> writeln(j);
> }
> }
>
> void wrapsFn(T...)(T args)
> {
> before();
> scope(exit) after();
> fn(args);
> }
>
> void main()
> {
> wrapsFn(2, 42, "a");
> }
>
Ok, but it's no longer a function. I can't do with wrapFn the same things I can do with a regular function, namely taking it's address and passing that around or calling through it later. If there were a simple and efficient way to convert the wrapsFn template into a function pointer then I'd be satisfied with that solution.
Comment #11 by larsivar — 2008-02-15T12:05:37Z
Jarrett Billingsley wrote:
> <[email protected]> wrote in message
> news:[email protected]/issues/...
>> http://d.puremagic.com/issues/show_bug.cgi?id=1839
>>
>> Summary: Give D a modern varargs system that allows forwarding
>> Product: D
>> Version: 2.010
>> Platform: PC
>> OS/Version: Windows
>> Status: NEW
>> Severity: enhancement
>> Priority: P2
>> Component: DMD
>> AssignedTo: [email protected]
>> ReportedBy: [email protected]
>>
>>
>> Given function
>>
>> void A(...) {
>> ...
>> }
>>
>> It should be possible to write a function wrapsA of this sort:
>>
>> void wrapsA(...) {
>> pre_call();
>> A(...); // or whatever syntax you want for forwarding the varargs.
>> post_call();
>> }
>>
>> That D -- a supposedly modern, easy-to-use languages -- can't do this
>> conceptually simple thing is sad.
>>
>> The whole point of procedural programming is that code is encapsulated
>> into
>> reusable blocks. Not being able to forward varargs means that a big
>> chunk of
>> that reusability is lost any time you write a varargs function.
>>
>> The workaround is to write every varargs function in two flavors, one
>> thats got
>> (...) args, and one that's got (_argptr,_arguments) args. But if the
>> solution
>> is so mechanical and straightforward then why can't the compiler at just
>> do
>> that for us? I'm not saying that's the right way to implement vararg
>> forwarding, but barring a more elegant solution, that sounds like at
>> least a
>> plausible solution.
>
>
> I'll suggest it again: given a typesafe variadic function whose variadic
> parameter's array element type is a struct:
>
> void A(Variant[] args...)
>
> calling that function:
>
> A(1, 2, "hi")
>
> should be sugar for:
>
> A(Variant(1), Variant(2), Variant("hi"))
>
> This alone would obviate the need for ... style vararg functions. A
> Variant array is far, far more useful than the platform-dependent
> hard-to-use
> inflexible _arguments/_argptr crap. And at the same time, solves the "OMG
> template bloat!!" problem.
Only that that would move Variant into the runtime which is unfortunate.
Comment #12 by larsivar — 2008-02-15T12:05:43Z
[email protected] wrote:
> http://d.puremagic.com/issues/show_bug.cgi?id=1839
>
> ------- Comment #4 from [email protected] 2008-02-15 02:36 -------
> "..."-style varargs predate template varargs and have a number of
> disadvantages compared to them, so it is likely they will get deprecated
> in favor of templates and macros.
>
>
> --
If there is absolutely no bloat when using such a template, only then will
it be acceptible to remove a runtime alternative.
Comment #13 by andrei — 2008-02-15T16:32:03Z
(In reply to comment #10)
> Ok, but it's no longer a function. I can't do with wrapFn the same things I
> can do with a regular function, namely taking it's address and passing that
> around or calling through it later. If there were a simple and efficient way
> to convert the wrapsFn template into a function pointer then I'd be satisfied
> with that solution.
I understand, and that's a good point. There can't be a conversion from that template to a delegate(...) without breaking type safety.
Here are some other thoughts.
1. The feature you are suggesting offers diminishing returns. It will help people who:
(a) write a ton of (...) functions so they hate the forwarding idea
(b) don't like macros that would partially automate (a)
(c) call a ton of (...) functions and work so any syntactical burden would be unacceptable
(d) are in an environment such that template bloat is an issue
There are things that D can't do today, and things that it can do in clunky ways that are encountered more often than the above. I think it's best we focus on those first.
2. That being said, I've tried to convice Walter of the utility of the "return goto" statement, which replaces one function entirely with another. That would be extremely cheap forwarding for a variety of cases, and would make tail recursion and mutual recursion obvious and documentable. If that feature were in, you'd have half of your needed feature:
void foo(...)
{
...
}
void callfoo(...)
{
before();
return goto foo;
}
You can't do an "after" action because the return goto statement effectively "execs" foo, and as such callfoo's frame ceases existence. This statement is easy to typecheck and generates good modular code, but it has certain limitations (e.g. callfoo cannot have scope() statements, locals with destructors etc.)
So this feature would cover some things, and yours would cover some overlapping things. I'm not sure whether either or both have critical mass.
Andrei
Comment #14 by wbaxter — 2008-02-15T17:04:51Z
(In reply to comment #13)
> (In reply to comment #10)
> > Ok, but it's no longer a function. I can't do with wrapFn the same things I
> > can do with a regular function, namely taking it's address and passing that
> > around or calling through it later. If there were a simple and efficient way
> > to convert the wrapsFn template into a function pointer then I'd be satisfied
> > with that solution.
>
> I understand, and that's a good point. There can't be a conversion from that
> template to a delegate(...) without breaking type safety.
>
> Here are some other thoughts.
>
> 1. The feature you are suggesting offers diminishing returns. It will help
> people who:
>
> (a) write a ton of (...) functions so they hate the forwarding idea
> (b) don't like macros that would partially automate (a)
> (c) call a ton of (...) functions and work so any syntactical burden would be
> unacceptable
> (d) are in an environment such that template bloat is an issue
(e) want to have a callback/signal-slot/observer/notification system capable of using varargs functions as callbacks.
> There are things that D can't do today, and things that it can do in clunky
> ways that are encountered more often than the above. I think it's best we focus
> on those first.
Agreed. I certainly didn't mean to imply by filing this that I thought this problem should be prioritized over other pressing issues. But it's something that I've long found annoying and seen discussed at various time, and yet there didn't seem to be a bug/enh filed for it yet.
But it seems in conclusion that you agree with me that this should not be marked "resolved invalid".
Comment #15 by andrei — 2008-02-15T17:33:16Z
(In reply to comment #14)
> (In reply to comment #13)
> > There are things that D can't do today, and things that it can do in clunky
> > ways that are encountered more often than the above. I think it's best we focus
> > on those first.
>
> Agreed. I certainly didn't mean to imply by filing this that I thought this
> problem should be prioritized over other pressing issues. But it's something
> that I've long found annoying and seen discussed at various time, and yet there
> didn't seem to be a bug/enh filed for it yet.
>
> But it seems in conclusion that you agree with me that this should not be
> marked "resolved invalid".
Oh. I planned to do so myself, but forgot while I was writing the post.
Andrei
Comment #16 by code — 2018-02-24T18:49:45Z
This won't work due to ABI specialities, things need to be setup for another call and it's unreasonable that other backends would support our special D vararg forward. Just get a va_list and forward that indead, it contains pointers to all the original arguments.