Boost logo

Boost :

From: Ruben Perez (rubenperez038_at_[hidden])
Date: 2025-05-05 09:23:08


On Sat, 3 May 2025 at 22:22, Jean-Louis Leroy via Boost
<boost_at_[hidden]> wrote:
>
> > 1. I've tried playing with custom RTTI, because that's what the clang AST
> > API uses. When implementing boost_openmethod_vptr, I used
> > default_policy::static_vptr<T>. But that seems to defeat the purpose of
> > policies and scoping - is there a way to obtain the policy that invoked
> > boost_openmethod_vptr?
>
> Ah, good point. I'm going to look into this. Since we cannot specialize function
> templates, I'll probably have to pass a Policy& as a parameter.
>
> Logically, with_vptr should usable several times in the same hierarchy, adding
> several vptrs to the objects. It makes sense, because external vptrs already
> permit associating several vpts to the same object.
>
> Can you share your code? My first idea would be a clang policy. I guess that you
> put a switch in boost_openmethod_vptr?

Since building with the clang API itself is a nightmare, I've created
a toy example, but with the same idea:

enum class kind : std::uintptr_t { unknown, n1, n2 };

class base_node {
    kind k_;

  protected:
    base_node(kind k) noexcept : k_(k) {
    }

  public:
    kind getKind() const {
        return k_;
    }
};

class node1 : public base_node {
  public:
    node1() noexcept : base_node(kind::n1) {
    }
};

class node2 : public base_node {
  public:
    node2() noexcept : base_node(kind::n2) {
    }
};

boost::openmethod::vptr_type boost_openmethod_vptr(const base_node& b) {
    switch (b.getKind()) {
    case kind::n1:
        return boost::openmethod::default_policy::static_vptr<node1>;
    case kind::n2:
        return boost::openmethod::default_policy::static_vptr<node2>;
    default:
        return boost::openmethod::default_policy::static_vptr<base_node>;
    }
}

[snip]

>
> > 3. Some of the complexity with policies (like needing a fork function) seem
> > to be stemming from the point above. What do you think? For instance,
> > couldn't facets be made regular members? Then add/fork can be implementing
> > by just inheriting from a base policy, using regular C++.
>
> Do you mean static or instance members? If it is the latter, then policies would
> become objects. Which we can pass as template parameters, but it would run into
> difficulties, because we don't have universal template parameters yet. A simple
> example: use_classes. It uses std::is_base_of to detect if the last class is a
> policy. So a policy has got to be a type (unless we change the contracts a lot).
>
> If you mean static members, we are back to the problem of separating my_policy's
> error stream from your_policy's.

I see, I meant static members. I was thinking of somehow splitting the
state part on another object - but it might complicate things further.

>
> > 4. I had the same impression as Joaquin that virtual_ should be used to
> > mark virtual arguments in method definitions, and virtual_ptr and regular
> > references in overriders. What do you think?
>
> So, to make sure that we are on the same page, you mean:
>
> BOOST_OPENMETHOD(poke, (std::ostream &, virtual_<Animal>), void);
>
> BOOST_OPENMETHOD_OVERRIDE(
> poke, (std::ostream & os, virtual_ptr<Cat> cat), void) { ... }

Yes, this is what I mean.

>
> The problem with this: BOOST_OPENMETHOD would need to foresee what the overrides
> will look like. It could be:
>
> BOOST_OPENMETHOD_OVERRIDE(
> poke, (std::ostream & os, Cat& cat), void) { ... }
>
> ...when using with_vptr. Or if the user doesn't need the best performance, maybe
> the body of the overriders does enough work to make the cost of the extra
> instructions irrelevant.

Yes, either of these would be valid:

BOOST_OPENMETHOD_OVERRIDE(poke, (std::ostream & os, Cat& cat), void) { ... }
BOOST_OPENMETHOD_OVERRIDE(poke, (std::ostream & os, virtual_ptr<Cat>
cat), void) { ... }

And I guess you'd need some if constexpr's to create the virtual_ptr
or not, and some logic to handle the case where the user declared both
of them, which should be illegal.

>
> A secondary benefit: with virtual_ptr in both declarations and overriders, it is
> easier to explain the (basic) rules. If you look at YOMM2's doc or my talks, I
> often talk about "peeling off" the "virtual_ decorators". With virtual_ptr at
> all levels, it is more straightforward to explain.

In regular C++ OOP, we already say "virtual" in base classes and
"override" in implementations, so I'm not sure about this.

> 5. I got the impression that virtual_ptr can't be used with custom RTTI. Is
> that true?
>
> No it's not, that would be very disappointing ;-)
>
> virtual_ptr obtains the vptr from the policy, which is the second template
> parameter with the default value BOOST_OPENMETHOD_DEFAULT_POLICY.

When explaining virtual_, the documentation states:

"By itself, virtual_ does not provide any benefits. Passing the
virtual argument by reference almost compiles to the same code as
creating a virtual_ptr, using it for one call, then throwing it way.
The only difference is that the virtual argument is passed as one
pointer instead of two.

However, we can now customize how the vptr is obtained. When the
method sees a virtual_ parameter, it looks for a boost_openmethod_vptr
function that takes the parameter (by const reference), and returns a
vptr_type. ..."

Which suggests that my boost_openmethod_vptr won't be called unless I
use virtual_. How would I write my example above so it calls
boost_openmethod_vptr?

Cheers,
Ruben.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk