Something like ADL from C++?

Manu turkeyman at gmail.com
Tue Dec 3 11:55:50 UTC 2024


Maybe someone has a pattern for doing this kind of thing...

So, I have a function that does something to a various set of things; let's
say we're going to serialise them or something:

```
module serialise;

void serialise(T)(void[] buffer, T)
  if (isSomeInt!T)
{ /* serialise integer */ }

void serialise(void[] buffer, const(char)[] str)
{ /* serialise string */ }

// ...etc
```

And some serialiser somewhere calls `serialise(thing)` for each thing,
where the type of thing chooses the right overload and we're all good...
sure, everyone knows this pattern.

So, I add a user thing in a module somewhere:

```
module app.user_thing:

struct UserThing
{ ... }

void serialise(void[] buffer, ref UserThing t)
{ /* serialise UserThing */ }
```

Now this thing wants to be serialisable, so you implement a serialise
function beside it...
In C++, this works; because ADL (argument dependent lookup) will cause to
additionally search the scope where the argument is defined for overloads.
Trouble is, in D unless `app.user_thing` was imported inside the serialiser
where it makes the call to `serialise()`, this overload won't be found,
because the UserThing overload is not in scope for the serialiser.

I tried to simulate something like ADL by getting `__traits(parent, value)`
in a loop until I find the module for non-builtin objects, and then have
the serialiser import that module prior to the call, to attempt to make
sure the argument's module is also in scope so any potential overloads can
be found when it tries to make the call:

import default_serialise : serialise;

void doSerialise(Things...)(void[] buffer, Things things)
{
  static foreach (thing; things)
  {{
    static if (isUserType!thing)
    {
      enum thingMod = getModuleForThing!thing;
      mixin(import " ~  thingMod ~ ";");
    }
    serialise(buffer thing);
  }}
}

The surprise is that if `thingMod` has a symbol `serialise`, it imports it
at the inner scope, and it shadows the global overload set rather than
complementing it...

So, I guess the thing I'm stuck on is; given there are some imports at
global scope, and it may have an overload set for some function; HOW can I
import more items to that overload set?

I tried this, but it doesn't work:

import default_serialise : serialise;

void doSerialise(Things...)(void[] buffer, Things things)
{
  static foreach (thing; things)
  {{
    static if (isUserType!thing)
    {
      enum thingMod = getModuleForThing!thing;
      mixin(import " ~  thingMod ~ ";");
      import default_serialise : serialise; // re-import at inner scope,
beside the other one
    }
    serialise(buffer thing);
  }}
}

Re-importing the globals into the same scope doesn't cause them to combine
either; just the symbol from whichever import statement appears first is
the winner...

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....
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20241203/d9198e21/attachment.htm>


More information about the Digitalmars-d mailing list