ADL

Manu via Digitalmars-d digitalmars-d at puremagic.com
Fri Sep 2 16:51:35 PDT 2016


On 3 September 2016 at 08:38, Walter Bright via Digitalmars-d
<digitalmars-d at puremagic.com> wrote:
> On 9/2/2016 5:15 AM, Manu via Digitalmars-d wrote:
>>
>> In C++, there is this ADL thing (argument dependent lookup).
>
>
> Yeah, I know about Koening lookup. It was a hack added to C++ to make
> operator overloading work.

Naturally you do, and I'm sure that is why it was invented, but you
couldn't write any modern C++ without it. The reason you say it was
invented is not the reason that it's useful.


>> D doesn't seem to have this,
>
>
> That's right, and it's on purpose :-)

And that seems to be a rather big problem.

The situation is this:
In D, it is ***EXTREMELY*** common to have some argument of type T,
like, basically everything in D is a template these days... we're
talking ranges and stuff.
It is also considered un-cool in modern D to aggregate all
functionality into types themselves. We want functionality for T to be
extensible, so we use UFCS all over the place.

Template args combined with UFCS practically demand ADL or something
similar to ADL, otherwise you can't really write algorithms. It's
impossible to import all the appropriate sources into the file that
implements the algorithm. They're unrelataed, except that the
algorithm is expected to 'work' on the T it's given. So if someone
supplies a T to your algorithm, and it's a range for instance (or
something following that pattern), and some part of it's
implementation is UFCS, it all falls apart :/

We can't have the situation "UFCS works quite nicely... in this
particular subset of common situations".


>> and that is proving to be quite problematic. What's the work around?
>
>
> Not a workaround, as D does not need ADL. This is how to do it:
>
> extern (C++, bob) {
>   struct S {}
>   void f(S s);
> }
>
> extern (C++, joe) {
>   struct T {}
>   void f(T t);
>
>   void test()
>   {
>     T t;
>     f(t); // obviously works, T is in the local namespace
>
>     alias f = bob.f;  // bring bob.s into current scope
>     bob.S s;
>     f(s);  // no problemo
>   }
> }
>
>
> The 'alias' construct gives good control over which symbols are visible in
> which scopes.

Now to put it in the terms I describe above, 'test()' is an algorithm,
implemented in a module unrelated to bob or joe (I should have given
the example with test() outside the namespace)... the algorithm
implementation can't go aliasing or importing anything relating to its
possible arguments T or S; it's meant to be generic.

module bob;
struct S {}
void f(S s);

module joe;
struct T {}
void f(T t);

module myalgorithm;
void test(T)(T t)
{
  f(t);
}


module user_code;
import bob, joe;
void main()
{
  test(S.init);
  test(T.init);
}

This is a better example. I can't be invading test() with any aliases,
or imports. It wouldn't be an algorithm anymore if I did that.
This pattern seems to bite me every direction I turn when trying to
write range or algorithm style code. C++ has ADL, and ADL works. I've
never thought about this problem in C++, or had any problems with ADL.


More information about the Digitalmars-d mailing list