Something like ADL from C++?
Bradley Chatha
sealabjaster at gmail.com
Tue Dec 3 15:31:43 UTC 2024
On Tuesday, 3 December 2024 at 11:55:50 UTC, Manu wrote:
> Has anyone ever experimented with a pattern like this?
> Essentially, I can't work out how to expand /combine an
> overload set to include symbols from multiple imports....
I gave this go using an eponymous template - it's definitely a
bit yucky, but seems to do as you want (would obviously need more
extensive work for real use cases rather than this simple POC):
## serialise.d
```d
module serialise;
import std.traits : isNumeric;
string serialise(T)(T foo)
if(isNumeric!T)
{
import std.conv : to;
return foo.to!string();
}
string serialise(string s)
{
return s;
}
string doSerialise(Things...)(Things things)
{
string slowBuilder;
static foreach (i, Thing; Things)
{{
enum hasSerialiser = __traits(compiles,
serialiserFor!Thing);
static if(hasSerialiser)
slowBuilder ~= serialiserFor!Thing(things[i]);
else
slowBuilder ~= serialise(things[i]);
}}
return slowBuilder;
}
template serialiserFor(Thing)
{
import std.traits : fullyQualifiedName;
mixin("import thingMod =
"~fullyQualifiedName!(__traits(parent, Thing))~";");
alias serialiserFor = thingMod.serialise;
}
```
## app.d
```d
module app;
import std.stdio;
void main()
{
import serialise;
import std : writeln;
writeln(doSerialise(UserThing("a")));
writeln(doSerialise("a"));
}
struct UserThing
{
string a;
}
string serialise(UserThing thing)
{
return "UserThing: " ~ thing.a;
}
```
## Result
```
UserThing: a
a
```
Namely:
The use of an eponymous template helps avoid the symbol
resolution issue - you could also try to mixin a local import
like `import userthing : userThingSerialise = serialise` instead,
but a template might be cleaner.
I use a `__traits(compiles)` ~~abuse~~ check since it's simple,
but you can probably also do some stuff with
[__traits(hasMember)](https://dlang.org/spec/traits.html#hasMember), `__traits(getMember)`, etc.
Another potential way if keeping the serialise function separate
from the actual type is mandatory, which I can't be bothered to
prototype, is to make a template struct similar to this:
```d
struct Serialiser(UserThingT, alias SerialiserFunc){...}
```
And then have a specific overload within the main serialiser
module to handle this case:
```d
// Something like this at least.
yada serialise(Thing)(...)
if(isInstanceOf!(Serialiser, Thing))
{
// Use SerialiserFunc against Thing?
}
```
Though there's probably a bunch of issues with lifetimes, const,
ref, etc. with that approach.
Definitely an interesting issue, though personally I'd try my
best to allow types to have a `serialise` function directly be a
part of them rather than using free standing extentions.
More information about the Digitalmars-d
mailing list