C++ Logo

std-proposals

Advanced search

Re: [std-proposals] A type trait for detecting virtual base classes

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Sun, 1 Oct 2023 20:55:07 -0400
On Sun, Oct 1, 2023 at 10:32 AM Giuseppe D'Angelo via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> Il 29/09/23 15:36, Arthur O'Dwyer via Std-Proposals ha scritto:
> >
> > (2) In the first para of section 2, rather than saying vaguely "to
> > complement the existing std::is_base_of trait," I'd prefer to see the
> > exact wording of the proposed trait placed right there. That way, the
> > reader won't be wondering about the intent for ambiguous/private bases;
> > they'll already know the destination you're trying to get to. (Full
> > disclosure: I always forget whether is_base_of considers ambiguous
> > bases. It does. I think this makes it unimplementable in pure C++,
> > right? it requires a compiler builtin?)
>
> There is an implementation of `is_base_of` in pure C++, courtesy of Boost:
>
> https://www.boost.org/doc/libs/1_83_0/boost/type_traits/is_base_and_derived.hpp
> I have, well, absolutely no idea why that code works, nor if there are
> shortcomings. In the paper I called is_virtual_base_of "experts-only",
> but that's suffering from arrogance, since even _I_ was able to
> implement it from scratch. I would never be able to come up with that
> is_base_of implementation...
>

Neat! I may try to dissect it later. I notice the big block comment refers
to some Usenet message-id
<df893da6.0301280859.522081f7_at_[hidden]>
but I'm no longer aware of any way to search for that message anywhere on
the public Internet. If anyone knows how to see what that message was,
please tell me!

You've changed the sentence to
> The trait that we are proposing will[,] however[,] have slightly
different semantics.
but that's still needlessly mysterious, IMO. It should just *say* what
those semantics are and how they differ from Boost's.

> E.g. "We propose the addition
> > of the std::is_virtual_base_of type trait to the Standard Library, to
> > detect when one class is (even ambiguously or privately) a virtual base
> > of another."
>



> > (5) In section (2.1) Prior Art, am I correct that Boost's type trait
> > can't deal with ambiguous bases? This would be worth mentioning, just to
> > clarify that the proposed trait will differ from the Boost
> implementation.
>
> That's correct. I've remarked this limitation now multiple times.
>

(Not your bug, but) It's actually bizarre, then, that Boost.TypeTraits has
an is_base_of that *can* detect private/ambiguous bases and an
is_virtual_base_of that *cannot*. I'd think the same technique would apply
to both. I hope to figure this out at some point. :)


> > (7) "Note 3: A class is never..." could say "type", instead of "class."
>
> Being non-normative, I've left `class`, as it should be clear from the
> context.
>

What I meant is: Your current wording claims that a *class* is never its
own base. But the true claim (AFAIK) is actually much stronger: AFAIK, it
is true that *no type at all *is ever its own base. There's nothing special
about *classes specifically* in this context, so you shouldn't use wording
that implies that there *is* something special about them. The claim
applies to all types, so you should just say "type". ...Unless there *does*
exist a (non-class) type which is its own base?


> One high-level concern which I /think/ is moot but it would be nice to
> > say so explicitly: Conversion from D* to B* requires dereferencing B*
> > when D is a virtual base of B. Is that a "when and only when"? Or are
> > there any other situations where that conversion might require
> > dereferencing B*, that we should be thinking about?
>
> Do you mean "requires dereferencing" by the implementation, as opposed
> as an actual dereference in the code?
>

Yes. By "[Foo] requires dereferencing B*," I meant "[Foo] receives a B*,
and if that B* points into unmapped memory then we should expect a
segfault."
And I think I should have said "D*" rather than "B*". :)

> - Things that can require inspecting a vtable include dynamic_cast,
> > typeid, and accesses through `int D::*` pointers-to-data-member that
> > happen to point into a virtual base.
>
> Doesn't calling a non-static non-virtual member function defined in the
> virtual base also require going into the vtable, again to find where the
> virtual base subobject is, and calculate `this` for the call?
>
> > struct B { void f(); };
> > struct D : ... inherit virtually from B ... {};
> > void call(D &d) { d.f(); }; // "actually" ((B &)d)::B.f(), with a bit of
> notation abuse
>

Yes.


> I'm not exactly sure what is the point here, was it merely to enumerate
> all the cases where an implementation needs to inspect an object's vtable?
>

I was trying to enumerate a bunch of operations that might surprisingly
require dereferencing a pointer (in the sense that if the pointer points
into unmapped memory then we should expect a segfault). I suppose there
could be other cases where the "surprise dereference" goes to something
other than the vptr/vtable, but I haven't immediately thought of any such
cases.


> > - Things that do pointer conversions include static_pointer_cast,
> > dynamic_pointer_cast, etc.
>
> Yes, but those are fine, they work on shared_ptr, not weak_ptr, and in
> there you know if the pointer is dangling or not. (I'd still like to
> have them on unique_ptr as well, but that's another story.)
>

Right.
(As I said to Thiago, I'm pretty sure I don't see any problematic cases in
the Cartesian product of *those* facilities; but I'm very unconfident that
I thought of *all* of the things. There might be things I missed that need
addressing in a similar vein.)

HTH,
Arthur

Received on 2023-10-02 00:55:21