Last active
June 17, 2020 16:18
-
-
Save pbackus/0a70419eb8bece52f3a08edfe7b6019b to your computer and use it in GitHub Desktop.
"Argument-Dependent Lookup" for D
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module example; | |
import extension; | |
import lib; | |
// Can extend types from other modules | |
bool empty(A a) { return false; } | |
char front(A a) { return 'a'; } | |
void popFront(A a) {} | |
// ...but can't hijack existing methods | |
char front(C c) { return 'x'; } | |
void main() | |
{ | |
import std.range: take, only; | |
import std.algorithm: equal; | |
A a; | |
assert(a.extended.take(3).equal(only('a', 'a', 'a'))); | |
B b; | |
assert(b.extended.take(3).equal(only('b', 'b', 'b'))); | |
C c; | |
assert(c.extended.take(3).equal(only('c', 'c', 'c'))); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Extended methods with C++-esque argument-depended lookup | |
module extension; | |
/// The name of the module that defines sym | |
private template moduleName(alias sym) | |
{ | |
alias parent(alias sym) = __traits(parent, sym); | |
enum bool isPackage(alias sym) = __traits(isPackage, sym); | |
string trimModuleName(alias sym)() | |
{ | |
static if (isPackage!sym) | |
return sym.stringof["package ".length .. $]; | |
else | |
return sym.stringof["module ".length .. $]; | |
} | |
static if (__traits(compiles, parent!sym)) { | |
static if (__traits(isModule, sym) || isPackage!sym) | |
enum string moduleName = moduleName!(parent!sym) ~ "." ~ trimModuleName!sym; | |
else | |
enum string moduleName = moduleName!(parent!sym); | |
} else { | |
enum string moduleName = trimModuleName!sym; | |
} | |
} | |
/// Inline import | |
private template from(string module_) | |
{ | |
mixin("import from = ", module_, ";"); | |
} | |
/// Finds a method using argument-dependent lookup | |
template extendedMethod(string method, string context = __MODULE__) | |
{ | |
auto ref extendedMethod(T, Args...)(auto ref T obj, auto ref Args args) | |
{ | |
import core.lifetime: forward; | |
static if (__traits(compiles, mixin("obj.", method, "(forward!args)"))) { | |
// Normal method | |
return mixin("obj.", method, "(forward!args)"); | |
} else static if (__traits(compiles, | |
__traits(getMember, from!(moduleName!T), method)(forward!(obj, args)) | |
)) { | |
// UFCS method from defining module | |
return __traits(getMember, from!(moduleName!T), method)(forward!(obj, args)); | |
} else static if (__traits(compiles, | |
__traits(getMember, from!context, method)(forward!(obj, args)), | |
)) { | |
// UFCS method from calling module | |
return __traits(getMember, from!context, method)(forward!(obj, args)); | |
} else { | |
import std.traits: fullyQualifiedName; | |
static assert(false, | |
"no extended method `" ~ method ~ "` found for type `" | |
~ fullyQualifiedName!T ~ "` in module `" ~ context ~ "`" | |
); | |
} | |
} | |
} | |
/** | |
* A wrapper that extends the methods of `T` to include UFCS "methods" from | |
* `T`'s module as well as the module given by `context` (by default, that of | |
* the calling code). | |
*/ | |
struct Extended(T, string context = __MODULE__) | |
{ | |
import std.meta: staticIndexOf; | |
T obj; | |
alias obj this; | |
auto ref opDispatch(string method, Args...)(auto ref Args args) | |
{ | |
import core.lifetime: forward; | |
return obj.extendedMethod!(method, context)(forward!args); | |
} | |
} | |
/// Factory function for `Extended` | |
Extended!(T, context) extended(T, string context = __MODULE__)(T obj) | |
{ | |
return Extended!(T, context)(obj); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module lib; | |
struct A {} | |
struct B {} | |
bool empty(B b) { return false; } | |
char front(B b) { return 'b'; } | |
void popFront(B b) {} | |
struct C | |
{ | |
bool empty() { return false; } | |
int front() { return 'c'; } | |
void popFront() {} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment