
On Sat, 3 May 2025 at 22:22, Jean-Louis Leroy via Boost <boost@lists.boost.org> 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.