Reputation: 5390
I want to do this (supported by this), but I'm hitting a tiny issue (watered down for your less-headachy-non-displeasure).
Let's say I'm a library writer, and I have these functions in a D file:
module mod_a;
import std.stdio;
void run(T)(T v) { writeln("Jigglypuff!"); }
void runrun(T)(T v) { run(v); }
And I have client code in another module in which I attempt to overload run
and call runrun
:
import mod_a;
void run(T:double)(T v) { writeln("Wigglytuff!"); }
void main() { runrun(1.0); }
This code results in 'Jigglypuff!' being printed rather than 'Wigglytuff!', which makes sense, because the definition of runrun
can only see the unevolved unspecialized form available to it in its module. I (and client code), however, would like to be seeing a 'Wigglytuff' rather than a 'Jigglypuff'.
In C++ I'd throw a namespace mod_a { ... }
around the specialization of run to show that the client code's run should be examined alongside my library code when trying to determine what the definition of runrun
calls, welcoming the can of worms that came along with such behavior.
Is there an idiomatic D-way to organize this such that the function run
may be intentionally hijacked? Specifically, I'd like to mimic the way C++'s global functions behave with ad-hoc specializations.
Upvotes: 7
Views: 219
Reputation: 5390
I found a weird way of doing it using mixins to move the namespace searched from library code to user code. From the perspective of the library-writer it's one extra function and one extra template type; from the perspective of the user it's one extra (somewhat annoying) line of code alongside one extra function call per instance of a type with global 'methods' outside of its module. Considering that this is a way of explicitly overriding a language feature, I think that's pretty good.
module wrappers;
mixin template wrapmix()
{
struct Wrap(T)
{
T* val;
auto opDispatch(string Name, A...) { return mixin("(*val)."~Name~"(a);"); }
}
auto wrap(T)(ref T val) { return Wrap!T(&val); }
}
This is then used to create a type that searches in whatever module it's been declared for the dispatch.
import mod_a;
import wrappers;
mixin wrapmix; ///< magic
void main() {
double val = 1.0;
runrun(wrap(val)); ///< note the 'wrap' call
}
While the above code wasn't explicitly tested, I tested the general approach on GDC and it appears to work.
Oh, and there's the shortcoming that as written this only handles lvalue references, but that can be fixed methinks with overloads of wrap
in module wrappers
.
Upvotes: 0
Reputation: 41857
In this example you are the authoring the library mod_a
so it would be relatively easy to modify it. But I can't help but think about the situation where you aren't the author of the library.
In which case the actual author of the library would probably either be happy that you cannot just do what you are trying to do... or actively wants to support what you are trying to do.
lets assume the library writer wants you to be able to "hijack" a function that he/she uses in his implementation. He or she would probably go about it differently; I would.
This is one area where I believe the encapsulation stories you linked and I just read describe exactly how to achieve the opposite situation of what you want here. This type of thing screams that it needs contract programming.
As a library author, I'd probably offer you an interface and possibly an abstract class, maybe even one or two concrete implementations, that you could use to do your thing. Someone else might give add a template or runtime parameter requiring a specific implementation as an argument. Yet someone else could add a lazy string delegate to the mix.
(my) conclusion: As a library author, there are options to make what you want possible. If it's not possible with your preferred library you'll probably end up filing a feature request.
Upvotes: 1
Reputation: 3216
//untested
module mod_a;
import std.stdio;
void run(T)(T v) if (!is(T : double)) { writeln("Jigglypuff!"); }
void runrun(T)(T v) { run(v); }
import mod_a;
void run(T)() if (is(T : double)) { writeln("Wigglytuff!"); }
void main() { runrun(1.0); }
Upvotes: 2