Address of overloaded functions
Artur Skawina
art.08.09 at gmail.com
Thu Jul 4 04:31:09 PDT 2013
On 07/04/13 01:31, Tyro[17] wrote:
> On 7/3/13 12:52 PM, Artur Skawina wrote:
>> import std.stdio;
>>
>> void foo(int a){ writeln("overload int"); }
>> void foo(long b){ writeln("overload long"); }
>>
>> auto pickOverload(alias FP, A...)() @property { typeof(FP(A.init)) function(A) fp = &FP; return fp;}
>>
>> void main()
>> {
>> auto b = pickOverload!(foo, long);
>> b(2);
>> }
>
> Often I see terse demonstrations of ingenious ways of doing things in the language but am at a lost as with regards to under what circumstances on would use such a feature. This happens to be one of those cases. Could you provide a couple circumstances where this would prove useful/handy?
Any time you need a pointer to an overloaded symbol - it's not possible
to simply take its address, because there isn't enough information to tell
which function you're interested in. Overload resolution happens at the call
site (by matching the argument list); if you're not calling the function
you need another mechanism to disambiguate. When taking an address of an
overloaded function C++ (and D) selects the right one by looking at the
context, ie the "target". But that means that you need to know the exact
signature of the function - which isn't necessarily the case, especially
with D's auto and templates. For example, you may not care what the return
type is; in D a type name can easily be longer than a page, or require
`typeof()`, so it can also be hard if not impossible to write down.
"pickOverload" lets you ignore all the uninteresting details - the compiler
already knows them - you only have specify the arguments you intend to
call the function pointer with.
Actually, while context alone is mostly enough for std-C++-w/o-extensions,
it's not really enough for D - eg the above pickOverload implementation
will lose the function attributes and linkage. Which is a much bigger
problem in D because "@safe" etc are often inferred. It's unreasonable to
require the type of the "b" variable to always be up-to-date, when foo's
signature can be changing relatively often, or even be unpredictable, like
when the code is inside a template.
So a better pickOverload implementation would be:
template ID(T...) { alias T ID; }
template getOverloads(alias F) {
alias getOverloads = ID!(__traits(getOverloads, __traits(parent, F),
__traits(identifier, F)));
}
auto pickOverload(alias F, A...)() @property {
static if (__traits(compiles, function(){typeof(F(A.init)) function(A) fp = &F;}))
enum typeof(F(A.init)) function(A) fp = &F;
else static if (__traits(compiles, function(){extern (C) typeof(F(A.init)) function(A) fp = &F;}))
extern (C) enum typeof(F(A.init)) function(A) fp = &F;
foreach (O; getOverloads!F)
static if (is(typeof(&O==fp))) if (&O==fp)
return &O;
assert(0);
}
This now no longer loses information and works with @safe/pure etc code.
But it's anything but obvious, and extending it to handle other calling
conventions wouldn't scale. Hence a trait that does the overload
resolution and returns the chosen one would be a better idea.
artur
More information about the Digitalmars-d-learn
mailing list