C++ Logo

std-proposals

Advanced search

Re: Type dependent namespace and symbols set control

From: Jean-Baptiste Vallon Hoarau <jeanbaptiste.vallon_at_[hidden]>
Date: Sat, 19 Dec 2020 14:58:50 +0100
Thanks a lot for such a detailed answer Arthur.

I had originally written a much bigger paper with more motivational
examples and comparisons to alternatives design, but I figured no one would
have taken the time to read it thoroughly, so I made it about 4x shorter,
which was I guess too much.

I think i should explain where I'm coming from with these ideas :
We lack ways to do customizations points and interface adaptation in C++ (this
talk of JeanHeyde Meneide <https://www.youtube.com/watch?v=aZNhSOIvv1Q> sums
up current practice pretty well, and none are satisfactory). In my opinion
this will continue to be the case until we get something along the lines of
type-classes (and this paper
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1900r0.html>provides
some reason for that) but I don't think further work will be done on
concept_map anytime soon.
At the same time, the incomprehensible, unhygienic rules of look-up
continue to damage codebases.
All of these problems boil down to a context-dependent association between
types and symbols : they are intrinsically linked, and a proper solution
must be a *joint *solution. This is a fundamental weakness of C++ and I
find the fact that almost no propositions has been done to address this
(except this paper
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1292r0.html>) a
bit worrying : along with reflection/metaprogramming (which might
themselves offer a way out), this is one of the first issues with C++ right
now, one that every developer from novice to library writer wrestle with.

Now onto addressing your points :

(1, 2) Thanks for explaining how to do motivational examples properly, I'll
try to remedy to this in a future version

(3) This idea of "associated anonymous namespace" is *precisely* to provide
a migration away from current ADL. The new proposed rule for ADL should
only be something that is adopted is the far future, once most programmers
have adopted the practice of putting non-members in the associated
anonymous namespace (and in order for that to happen, there should be some
immediate benefits to doing so, for example by prioritizing (see (15)) the
functions found in the associated anonymous namespace over any viable
overload elsewhere).

(4, 5) You're right, those examples were not very good.

(6) The intent behind declaring a type-namespace final is to make the
symbols set of a type more observable, but I haven't really thought it
through and the same could be achieved by grepping an anonymous associated
namespace in a codebase. I'll ditch this part.

(8) What i wanted to say in this part is : when the override of a virtual
namespace N refer to N, N is considered as non-virtual : no specialization
is ever considered. This is solely for supporting reuse/partial override.
This is indeed completely at odds with how templates usually works.

(9) There is no necessary relations between the name of a namespace
specialization and its primary (i adopted the same logics as Matt Calabrese
paper here, linked above)

(12) Oh... just when i thought i knew everything there is to know about how
C++ look-up works. I had not taken that into account.
>What should the effect of "not using namespace N;" be, if namespace N
hadn't been "using-ed" to begin with?
No effect, this should just exclude every symbols from N from the current
set : if there is no intersection, that's ok as the intent is realized.

(14) I adopted the notation "namespace(T)" with the idea that until the
proposed rule for ADL is adopted (so in +10 years), the set of symbols
associated with T will be a superset of the associated anonymous namespace.
I.e. "namespace<T>" is a subset of "namespace(T)", until the rule is
adopted.

(15) by prioritization i meant : if a function X from a namespace N is
prioritized, then upon unqualified call to X, N::X will always be called if
possible, regardless of whether or not a better match exists elsewhere.
(the same rule with which members/non-members would be prioritized w.r.t.
notation in Bjarne UFC proposal)

I hope this makes a better case for these ideas.

Regards,
-Jean-Baptiste


Le sam. 19 déc. 2020 à 02:22, Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]> a
écrit :

> On Thu, Dec 17, 2020 at 5:07 PM Jean-Baptiste Vallon Hoarau via
> Std-Proposals <std-proposals_at_[hidden]> wrote:
> >
> > Recently i've been pondering on ADL, customization points and interface
> adaptation quite a bit.
> > (TLDR there's a paper embedded in this email that try to address these
> problems, let me know what you think)
>
> I've read your attached paper, "Type-namespace and symbols set control." I
> think:
> (1) It's got way too many new features bundled together in one paper. Are
> they all needed, or are you sneaking in some "nice-to-haves" among the
> "must-haves"? Which are which?
> (2) The features are poorly motivated. I mostly understand what each of
> them does, physically, but I don't understand why I as a C++ programmer
> would want to do those things. I recommend using "Tony Tables" to show (on
> the left) today's C++20 code that accomplishes some realistic task, and
> then (on the right) how the same task would be solved in a simpler or more
> robust way using your new features. The important thing in a Tony Table is
> that the left-hand column must be real code that people are writing today.
> If your proposal simplifies the writing of only some pieces of code that
> people don't actually want to write, then your proposal doesn't help.
> (3) I don't see a migration path from "C++20 ADL" to "your thing." IIUC,
> you are essentially proposing to take today's working code
>
> namespace N {
> struct A {};
> void swap(A&, A&);
> } // namespace N
>
> void test() {
> N::A a, b;
> using std::swap;
> swap(a, b); // should call N::swap, not std::swap
> }
>
> and silently break it. You write:
> "In order for a function F to be implicitly included in the set of
> available functions for unqualified look-up, F must be available in the
> associated anonymous type-namespaces of the types of the arguments."
> So, either you are proposing a rule by which N::swap becomes "available in
> the associated anonymous type-namespace" of N::A, or else you're proposing
> to break this existing code. I don't see the former; I suspect the latter,
> which is a non-starter for WG21.
>
> Longer/nittier comments:
>
> (4) Your moo(cow) example in section 1.1 is isomorphic to
>
> https://quuxplusone.github.io/blog/2018/08/13/fixing-adl-field-test/#update-tomasz-kami-ski-points-ou
> No less a personage than Herb Sutter has also proposed breaking this code,
> in P0934R0 "A Modest Proposal: Fixing ADL." But, if you're proposing to
> break it, you should clearly and explicitly say so, and show this example
> (or something like it). Your current example with names like `my_type` and
> `moo` is too contrived.
>
> (5) Also in that example, you say "Under the current ADL rules..." but
> that's meaningless, as your example code uses syntax that is not C++20
> syntax — `template<class T> namespace <my_type<T>>` and so on. So there's
> no way of telling what the "current" rules would make of it.
>
> (6) You write: "An anonymous type-namespace can be extended, unless
> declared `final`." This is meaningless. Namespaces can't be declared
> `final` today, and I don't see any grammar in your proposal that would make
> it possible in the future. For another thing, namespaces are open by
> definition; suppose I have two translation units, and in one I define
> `namespace A final { int foo(); }` and in the other I define `namespace A {
> int bar(); }`. Neither TU is aware of the other. Are you proposing that
> this should just be IFNDR?
>
> (7) In section 1.2's example, I recommend getting rid of the functions'
> bodies. Just put their declarations:
> template <class T>
> virtual namespace plot_traits {
> Point2D get_plot_point(T&, int);
> int get_num_points(T&);
> }
> template <class T>
> namespace complex_plot final : override
> plot_traits<std::vector<std::complex<T>>> {
> Point2D get_plot_point(T&, int);
> // OK as plot_traits<...> inside a plot_traits override always
> refer to the primary namespace
> using plot_traits<std::vector<std::complex<T>>>::get_num_points;
> }
> Incidentally, I've moved the `final` keyword to a more appropriate place
> (I assume that's what you intended).
>
> (8) Your code comment there is completely at odds with how templates work
> anywhere else in the language. Normally, when I have a template with some
> partial specializations, the partial specializations are there specifically
> to *avoid* instantiating the primary template for some problematic type.
> You seem to be proposing that when plot_traits<X> refers to plot_traits<X>,
> the compiler should take that as a sort of "super class reference," and go
> and instantiate the *next most specialized* specialization of the
> template. What happens if there are multiple partial specializations
> available, and it's not clear which one is the next most specialized?
>
> (9) Finally, where did the identifier `complex_plot` spring from? What is
> its purpose? What is the relationship between this "specialization" of
> namespace plot_traits<T> and the actual namespace complex_plot?
> namespace complex_plot { int baz(); }
>
> (10) The punchline of section 1.2 seems to be that after jumping through
> all of these hoops, we can then use plot_traits<T>::get_num_points(t)
> exactly as if plot_traits were defined as a plain old traits class, like
> `iterator_traits` or `regex_traits`:
>
> template<class T>
> struct plot_traits;
>
> So what's the purpose of all this new syntax? What does it buy us?
> Certainly not simplicity!
>
> (11) In section 1.3, you should read
> https://brevzin.github.io/c++/2019/04/13/ufcs-history/ and then eliminate
> section 1.3.
>
> (12) Section 2.1, "not using", is interesting.
> Do you understand the subtle difference between "using N::foo;" and "using
> namespace N;"? (I owe a blog post on this subject, although honestly I
> don't understand the reason it was done this way to begin with. Asked on
> SO:
> https://stackoverflow.com/questions/65365681/why-does-cs-using-namespace-work-the-way-it-does
> )
> What should the effect of "not using namespace N;" be, if namespace N
> hadn't been "using-ed" to begin with?
>
> (13) Section 2.2 lacks motivation. Remember, "motivation" isn't "Without
> this feature, my sample code won't compile!" and it isn't "This keyword
> kind of looks like it might do X, so let's make it do that." You should
> write a Tony Table, showing some real code from your codebase on the left,
> and then showing how you want to be able to write it, on the right. Make
> sure the snippet is long enough that it's "falsifiable." You want the
> reader to be able to say, "Ah, yes, I *agree* that that is the best
> possible way to write that code in C++20, and I could not do better myself;
> and furthermore, I agree that the right-hand snippet is better." If your
> snippet is too short and contrived, the reader will simply *disagree*
> that the left-hand snippet is reasonable.
>
> (14) Section 2.3, I don't understand how this `namespace(T)` relates back
> to your original idea of having an "anonymous associated namespace" named
> `namespace<T>`. Are `namespace(T)` and `namespace<T>` the same thing? If
> so, pick one name and stick to it. Don't make two names for the same notion.
>
> (15) Section 2.4 lacks motivation, and also lacks rigor — what do you mean
> by "prioritization" in this context? (But first, write a Tony Table! If you
> can't do that, then there's no point trying to explain what you meant by
> "prioritization.")
>
> (16) Section 2.4 also seems to be assuming some kind of UFCS. In C++
> today, `t.blah()` never calls a free function.
>
> (17) Section 2.5 lacks motivation.
> See D&E section 17.4, where Stroustrup writes:
> > ...the original design allowed for several member names to be mentioned
> in a single using-declaration:
> > using X::(f,g,h);
> > This is syntactically ugly, and so were all the alternatives we
> considered. ... Having used namespaces
> > a bit, I found far less need for such lists than I had expected. I also
> tended to overlook such lists when
> > reading code because they resembled function declarations too much, so I
> fell into the habit of using
> > repeated using-declarations instead:
> > using X::f;
> > using X::g;
> > using X::h;
> > Consequently, there is no special form of a using-declaration that
> specifies a list of member names.
>
> I buy Stroustrup's logic.
>
>
> Bottom line: I'm glad you're aware that lookup in C++ is a big mess with
> no logic. But it seems like you're just throwing a grab-bag of additional
> widgets at the wall, without actually trying to solve any existing problem.
> *And* you're proposing silent code breakage with no migration path.
>
> HTH,
> Arthur
>

Received on 2020-12-19 07:59:04