Is there any good reason why C++ namespaces are "closed" in D?

Manu turkeyman at gmail.com
Sat Aug 4 07:45:42 UTC 2018


On Wed, 1 Aug 2018 at 16:05, Walter Bright via Digitalmars-d
<digitalmars-d at puremagic.com> wrote:
>
> On 8/1/2018 10:24 AM, Jonathan M Davis wrote:
> > Not to say that that can't work, but I have to say that it seems pretty ugly
> > if using extern(C++, NS) requires a bunch of aliases just to use symbols
> > normally.
>
> What is normal is a slippery concept, especially when one is comparing different
> lookup rules between languages. D modules do not a 1:1 correspondence with C++
> namespaces, either (not even close).
>
> Aliases are a normal and highly useful D tool to copy names from one scope to
> another.
>
>
> > Certainly, it does come across like
> > you didn't trust the D module system to do its job for some reason.
>
> Reorganizing the code into modules means potentially forcing users to split code
> from one C++ file into multiple D files. How's that really going to work if you
> have a translation tool? One of the aspects of Java I didn't care for was
> forcing each class into its own file.
>
> So while Manu is clearly happy with cutting up a C++ file into multiple D files,
> I doubt that is universal. His proposal would pretty much require that for
> anyone trying to work with C++ namespaces who ever has a name collision/hijack
> or wants to make the code robust against collision/hijacking.
>
> An example of silent hijacking:
>
>     extern (C++, "ab") void foo(long); // original code
>     ... lots of code ...
>     extern (C++, "cd") void foo(int); // added later by intern, should have been
>                                       // placed in another module
>     ... a thousand lines later ...
>     foo(0); // OOPS! now calling cd.foo() rather than ab.foo(), D sux
>
> You might say "nobody would ever write code like that." But that's like the C
> folks saying real C programmers won't write:
>
>      int a[10];
>      for (int i = 0; i <= 10; ++i)
>         ...a[i]...
>
> But we both know they do just often enough for it to be a disaster.
>
> Now, with D:
>
>      extern (C++, ab) void foo(long);
>      foo(0);    // works!
>      ---
>      extern (C++, ab) void foo(long);
>      extern (C++, ab) void foo(int);   // error!
>      ---
>      extern (C++, ab) void foo(long);
>      extern (C++, cd) void foo(int);
>      foo(0);    // error!
>
> I juxtaposed the lines so it's obvious. It's not so obvious when there's a
> thousand lines of code between each of those lines. It's even worse when
> foo(long) sends a birthday card to your daughter, and foo(int) launches nuclear
> missiles.
>
> Yes, this extra protection comes at a cost - you'll have to type a bit more.
> Just like D doesn't allow implicit declaration of variables, requires static
> typing, and variables are always initialized unless you explicitly use `= void`.
> The protection is worth it, and is part of D's philosophy.
>
> I hope that adequately answers the "for some reason" :-)

I'm sorry, that doesn't come remotely close justifying the imbalance.

Firstly, it remains entirely hypothetical, whereas our criticisms are
evidence based, over more than half a decade.

"D modules do not a 1:1 correspondence with C++ namespaces, either
(not even close)." - exactly. So don't even try to emulate C++'s
broken feature in D. We can't win; equivalent semantics are
impossible, we only get the *worst* of both worlds by trying.

"Reorganizing the code into modules means potentially forcing users to
split code from one C++ file into multiple D files" - Yes, that is
what people do when binding C++ code.
We're not writing C++ code, we're writing D code, intended to be
consumed by other D code, playing by D's rules. We just want to be
able to call functions that are in some C++ .obj file, and get on with
writing our D code.
To make a C++ API comfortable and natural to a D user, in all but the
simplest cases, it has required such human intervention in all
meaningful case I've attempted so far, if you want a quality result.
I'm not interested in mediocre results; that does nothing but damage
the impression D makes on C++ users, and thereby wasting my time and
effort, and undermining the potential advance of D to a target group
of interested new users.

"So while Manu is clearly happy with cutting up a C++ file into
multiple D files, I doubt that is universal" - support this claim? I'm
happy with doing what is necessary to make a C++ API express as
naturally as possible to D code. I would expect most people making
such an effort to feel the same way. Why would you suspect otherwise?
But then also take that in conjunction with the fact that I haven't
actually argued for the demolition of the C++ namespace scope (I don't
care either way). It can stay and resolve these hypotheticals if you
must, although my bias is that the namespace should be applicable by
some other opt-in mechanism. But I'm not interested in pushing that, I
have no horse in that race.
You can keep your namespace. But don't ruin C++ interaction for the rest of us!

"How's that really going to work if you have a translation tool?" -
that sounds like a job that's 100 times simpler for a tool than for a
human!
That said, I have dabbled with such machine translation, and it's
non-trivial in a whole variety of ways. This issue you describe is
trivial by comparison to the real challenges; C++ and D are not the
same language, and they express differently. Lots of C++ constructs
don't have a perfect translation and need user intervention of one
kind or another.
Purely tool translated API's are likely to be poor-quality API's in D.
Lots of supporting machinery (macros, helper templates, etc) outside
of the hard symbols that you link against need to be massaged by hand
for natural D consumption.
Atila has done much more work on this, and this thread is evidence
that he's also been aggravated by this issue. He may have been able to
work-around, but we have wasted his time here, and nobody is any
better-off for it.
Your machine translation argument does not appear to track reality.

"Yes, this extra protection comes at a cost - you'll have to type a
bit more." - ignoring that this is 100% dismissive of every complaint
ever arisen... this isn't always possible. "Typing" isn't a feature of
generative programming, where the case must be detected and manually
accounted in the generational logic, which I do heaps of. A mixin
placed inside a namespace scope is incapable of also introducing a
matching alias outside of the scope. The acrobatics requires to make
this work undermine the value.

And this claim "The protection is worth it" - that is so much more
subjective than anything you've ever criticised me for saying, because
it flies in the face of all the evidence we've ever collected.

Your examples with multiple sibling namespaces in the same file are
borderline contrived. I've never seen sibling namespaces in a single
file. It's theoretically possible, but you can't balance the value
against the costs on the merit of that point with a straight face.
Occasionally there's some nested 'internal' namespace, and that maps
perfectly naturally to a submodule, or much better, lifted to private
or package protection at module scope. 'internal' or 'detail'
namespaces are a symptom of C++ not having such a feature, and it's
mostly not desirable to mirror them in D.
If you don't like that, you have previously suggested solutions where
we use an alternative construct like a struct for namespacing. But if
you genuinely feel a within-a-single-module namespace solution is
useful, then that should be a separate proposal that stands on its
own. It should also be opt-in.
I would still find no reason to deviate from the simplicity of
arranging namespaces via D modules, in a completely conventional,
consistent, and proven manner. The current design denies us this most
simple of options.

If that's the substance of the protective measures provided, then
that's a very poor balance against the problems.
Design should encourage maximum simplicity, not maximum complexity as it does.


More information about the Digitalmars-d mailing list