September 02, 2016 ADL | ||||
---|---|---|---|---|
| ||||
In C++, there is this ADL thing (argument dependent lookup). What it does is, when searching for overloads, in addition to looking in the local namespace, it also looks in the namespace of the function arguments. D doesn't seem to have this, and that is proving to be quite problematic. What's the work around? C++ example: namespace bob { struct S {}; void f(S s); } namespace joe { struct T {}; void f(T t); void test() { T t; f(t); // obviously works, T is in the local namespace bob::S s; f(s); // local namespace can't see `void f(S)`, but given the argument 's', which is typed bob::S, it will search the bob:: namespace for overloads of f(), so this code compiles successfully. } } I have the same configuration across 2 modules in D. In one module, I receive the foreign modules type via template arg, but I haven't imported that type's module, so when I try to call the function it can't find the overload, because it's not imported, and it doesn't search the argument type's module (ie, namespace) for overloads. |
September 02, 2016 Re: ADL | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On Friday, 2 September 2016 at 12:15:25 UTC, Manu wrote:
> D doesn't seem to have this, and that is proving to be quite problematic. What's the work around?
Somehow pass in the required information along with the symbol, for example as a member function/alias or an UDA. It's hard to get more specific than that without a concrete example.
— David
|
September 02, 2016 Re: ADL | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu Attachments:
| On 09/02/2016 03:15 PM, Manu via Digitalmars-d wrote: > In C++, there is this ADL thing (argument dependent lookup). > What it does is, when searching for overloads, in addition to looking > in the local namespace, it also looks in the namespace of the function > arguments. AFAIR it is intentionally not supported to simplify symbol lookup rules. > I have the same configuration across 2 modules in D. > In one module, I receive the foreign modules type via template arg, > but I haven't imported that type's module, so when I try to call the > function it can't find the overload, because it's not imported, and it > doesn't search the argument type's module (ie, namespace) for > overloads. Don't know about best practice but I tend to simply find the host module for the argument and mixin the import for it inside the template to make symbols directly available. |
September 02, 2016 Re: ADL | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On 9/2/16 8:15 AM, Manu via Digitalmars-d wrote:
> In C++, there is this ADL thing (argument dependent lookup).
> What it does is, when searching for overloads, in addition to looking
> in the local namespace, it also looks in the namespace of the function
> arguments.
>
> D doesn't seem to have this, and that is proving to be quite problematic.
> What's the work around?
>
> C++ example:
>
> namespace bob {
> struct S {};
> void f(S s);
> }
>
> namespace joe {
> struct T {};
> void f(T t);
>
> void test()
> {
> T t;
> f(t); // obviously works, T is in the local namespace
>
> bob::S s;
> f(s); // local namespace can't see `void f(S)`, but given the
> argument 's', which is typed bob::S, it will search the bob::
> namespace for overloads of f(), so this code compiles successfully.
> }
> }
>
>
> I have the same configuration across 2 modules in D.
> In one module, I receive the foreign modules type via template arg,
> but I haven't imported that type's module, so when I try to call the
> function it can't find the overload, because it's not imported, and it
> doesn't search the argument type's module (ie, namespace) for
> overloads.
>
This is a limitation, you can only use struct members given a type, you can't use UFCS functions or regular calls like the above. You need to import the module in the template *definition* file, which is problematic.
I think I remember seeing vibe.d do some funky shit to work around this, like getting the fully qualified name, and using string mixins to import the module that defines that type.
I think it would be nice to see this fixed, but I'm not sure of the implications.
One possibility:
import __traits(moduleOf, T);
Then you could do this import as a local import when you need it. Of course, this is not as nice as just having the compiler do this automatically.
-Steve
|
September 02, 2016 Re: ADL | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On Friday, 2 September 2016 at 12:15:25 UTC, Manu wrote:
>
The only problem I have with this feature is that it would lead to implicit-importation, which is a totally foreign concept in D, and I would assume a design choice to not support it.
import bob : S;
S s;
// implicit `import bob : f;` for this statement:
f(s);
Is this an acceptable feature? Perhaps; my main concern is that it can be very difficult to work out where this `f` symbol is coming from. If you see the only import statement is `import bob : S;` then you'd naturally assume that the `f` being called here is not `bob.f`.
It's nice to learn this term "argument dependent lookup" though. I've spent a lot of time thinking about this feature, but never realised it had a name (and I never realised any languages supported it).
|
September 02, 2016 Re: ADL | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | 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. > D doesn't seem to have this, That's right, and it's on purpose :-) > 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. |
September 03, 2016 Re: ADL | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On 3 September 2016 at 08:38, Walter Bright via Digitalmars-d <digitalmars-d@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. |
September 02, 2016 Re: ADL | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On 9/2/2016 4:51 PM, Manu via Digitalmars-d wrote: > (I should have given the example with test() outside the namespace) It's always best to provide an example of the actual problem rather than something else. > 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; import myalgorithm; // needed > 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++, First solution: module bob; struct S { void f(); } Second solution: module user_code; import bob, joe; import myalgorithm; mixin myalgorithm.test!S; mixin myalgorithm.test!T; void main() { test(S.init); test(T.init); } Third solution: module myalgorithm; void test(M,T)(T t) { M.f(t); } module user_code; import bob, joe; import myalgorithm; void main() { test!bob(S.init); test!joe(T.init); } Fourth solution: module myalgorithm; void test(T)(T t) { import std.traits; mixin("import " ~ std.traits.moduleName!T ~ ";"); mixin("alias M = " ~ std.traits.moduleName!T ~ ";"); // The above could be encapsulated into an eponymous template // that takes T as a parameter and returns the alias M.f(t); } > or had any problems with ADL https://en.wikipedia.org/wiki/Argument-dependent_name_lookup#Criticism Essentially, ADL has awkward problems when getting beyond the simple cases. It isn't right for D. |
September 03, 2016 Re: ADL | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On Saturday, 3 September 2016 at 01:09:18 UTC, Walter Bright wrote:
>
> Essentially, ADL has awkward problems when getting beyond the simple cases. It isn't right for D.
I could not agree more strongly!
If this feature were supported, it would probably break our module system.
Even if we could shoehorn it into the language it would make the compiler slower.
|
September 02, 2016 Re: ADL | ||||
---|---|---|---|---|
| ||||
Posted in reply to Stefan Koch | On 9/2/2016 6:12 PM, Stefan Koch wrote:
> If this feature were supported, it would probably break our module system.
> Even if we could shoehorn it into the language it would make the compiler slower.
Note that C++ needs ADL in part because it cannot do options 2, 3 or 4.
|
Copyright © 1999-2021 by the D Language Foundation