![]() |
Boost : |
From: Joaquin M López Muñoz (joaquinlopezmunoz_at_[hidden])
Date: 2025-04-29 16:35:52
El 27/04/2025 a las 15:15, ÐмиÑÑий ÐÑÑ
ипов via Boost escribió:
> Dear Boost community. The peer review of the proposed Boost.OpenMethod will
> start on 28th of April and continue until May 7th.
This is my review of the Boost.OpenMethod proposal. Thanks to Jean-Louis
for
his work and to Dmitry for managing!
As a guide to this review, I'm answering the points proposed by the
review manager
succintly and then a full review section is given at the end with much
more detail
(in no particular order).
* What is your evaluation of the design?
I find it adequate and well thought out, maybe leaning on overcomplexity for
the sake of covering as much as possible (below).
* What is your evaluation of the implementation?
I didn't look closely, but what little I saw looked to me robust. Code
looks clean, too.
* What is your evaluation of the documentation?
Quite extensive and probably covers anything that needs be covered, but
I have qualms about it (below).
* What is your evaluation of the potential usefulness of the library?
It is very useful if you need open methods, obviously. Personally I haven't
delved much into the world of virtual functions (I'm more of a template
programmer), but the fact that the predecessor of this library (YOMM2)
has real users is enough to convince me that the proposal fills a need.
* Did you try to use the library? With what compiler? Did you have any
problems?
Yes, to get my head around it by writing small snippets. VS 2022.
No problems detected.
* How much effort did you put into your evaluation? A glance? A quick
reading? In-depth study?
I spent 11 hours playing with the library and reading and re-reading
the docs.
* Are you knowledgeable about the problems tackled by the library?
I'm familiar with open methods since I read about them in Alexandrescu's
2001 "Modern C++ Design" (where they were called multimethods).
I think I'm sufficiently familiar with virtual function machinery to
understand
the technical challenges met by this proposal and the solutions adopted.
* Do you think the library should be accepted as a Boost library?
Yes, I vote to ACCEPT Boost.OpenMethod. I have some
observations/reservations about it, some of them more important than others.
To be clear, I'm not conditioning my vote on these observations being
addressed, since I know the author will ponder them carefully and respond
in the most appropriate way --which may not be the way I suggest.
FULL REVIEW
Main points:
* The docs are quite hard to read (to me, at least). Mainly, I miss clear
introductions and explanations of the many concepts involved before using
them in the tutorial. A (probably incomplete) list of concepts that I had to
wrap my head around is:
 + virtual_ptr, double role as a fat reference and as a virtual
argument marker
 + virtual_
 + compiler
 + guide function
 + policies and facets
* Policies, in particular, are introduced in a way that feels to me
haphazard
and incomplete. I never can be sure if I have all the policy-related
information
correctly and exhaustively.
* Iâd encourage the author to try and remove from the docs (i.e. not
disclose
publicly) as many utilities as possible â I have the feeling some
components are
described that donât really belong in the public API, though I may be
wrong on
this one.
* I think virtual_ptr is a misnomer and the entity should be renamed to
virtual_ref or something similar.
* I challenge the need to have virtual_shared_ptr, virtual_unique_ptr
and the
like. In my opinion, the proposal need not conflate virtual arguments with
object lifetimes, the latter being orthogonal to the purpose of the library.
* The decision to call an arbitrary override when thereâs a tie-in looks
erroneous
to me, and it may at least be controlled by some policy facet.
* The library relies very heavily on macros, and the macro-free alternative
does not seem too practicable. I guess this canât be helped, but itâd be
nice
if some thought were given to how to alleviate this overdependence on
macros.
Elaboration and full list of observations:
* virtual_ptr. I have a number of problems with this class:
 + The documentation starts using it right away without telling the
user what it
is about. At first, I thought it was merely a syntactic marker to
indicate wher
a virtual argument happens, which is the case, but not the whole story.
 + Its semantics are not those of a pointer, but behave more like a
reference.
This is explicitly acknowledged later in the docs, on the grounds that
virtual_ref,
which is a more apt name, is reserved for potential evolutions of the C++
language to include overloading of the dot operator. I think it is extremely
unlikely that this C++ feature will ever be realized.
 + As it happens, an open method works equally well if a reference to
a class is
passed instead of a virtual_ptr (this is apparent as soon as one
realizes that
virtual_ptr can be constructed from a plain reference, but I didnât read
that far
into the reference). In a private communication with the author, he told
me this
is indeed the case but virtual_ptr is more efficient because it stores
an extra
direct pointer to the associated virtual table, in a manner not
dissimilar to what
Rustâs fat pointers do (again, this is mentioned later in the
reference). I was led
astray by the implicit assumption that virtual_ptr was a sort of pointer,
which is not.
 + Whatâs the point if having virtual_ptr() and virtual_ptr(nullptr_t)
constructors
if virtual_ptr behaves as a reference? Same with the move constructor,
whatâs the
point of emptying the source virtual_ptr?
 + virtual_ptr<const C> is constructible from virtual_ptr<C> but not
the other
way around, as it should be. This is not readily apparent from reading the
reference, though.
 + All in all, I strongly recommend that virtual_ptr be renamed to
virtual_ref, and
that an early explanation in the docs is given for its presence as
opposed to using
plain references. operator -> can be retained because it provides a
terse syntax
to access the underlying object, but consider providing as well some value()
member function and/or a conversion operator to the underlying object.
* âMultiple and virtual inheritance are supported, with the exception of
repeated inheritanceâ: I donât know what repeated inheritance means âI may
make an educated guess, but I donât think this is standard terminology.
* Instead of BOOST_OPENMETHOD(poke, (std::ostream&,
virtual_ptr<Animal>), void),
consider the possibility of using
BOOST_OPENMETHOD(poke, void(std::ostream&, virtual_ptr<Animal>)).
* When virtual_ptr is used, classes are required to be polymorphic (i.e.
classes
with a virtual table), which is perfectly ok. But if I fail to declare
any virtual
function in my class hierarchy, simple snippets of code still compile and
produce spurious results. It would be good if this could be caught and
signaled
by a compile-time error.
* policies:debug and policies:release: I understand that these do not mix
together? I mean, if a DLL is compiled in debug mode, its exported open
methods wonât be taken by an executable compiled in release mode, right?
Not sure if this is expected and/or desireable.
* âIf there is still no unique best overrider, one of the best
overriders is chosen
arbitrarily.â Iâm not sure this is the best default option, probably a
run-time
error is more appropriate here, maybe controlled by some facet of the
policy.
* In the description of the release policy, itâs stated that, for
instance, the facet
extern_vtpr is implemented with vptr_vector and vtpr_map. This doesnât make
much sense to me, as a policy canât possibly provide more than one
implementation for a given facet. Looking at the source code I learnt
that itâs
vptr_vector thatâs used for extern_vptr in release.
* âIf BOOST_OPENMETHOD_DEFAULT_POLICY is defined before including this
header, its value is used as the default value for the Policy template
parameter
throughout the code. Otherwise, boost::openmethod::default_policy is used.â
I think it woud be more correct to say âOtherwise,
BOOST_OPENMETHOD_DEFAULT_POLICYÂ is defined to
boost::openmethod::default_policyâ.
* In the reference, initialize is declared as:
template<class Policy = BOOST_OPENMETHOD_DEFAULT_POLICY>
auto compiler<Policy>::initialize() -> /*unspecified*/;
I understand that this should be
template<class Policy = BOOST_OPENMETHOD_DEFAULT_POLICY>
auto initialize() -> /*unspecified*/;
* âOpenMethod supports dynamic loading on operating systems that are
capable of handling C++ templates correctly during dynamic link.â I donât
understand what this means, and what operating systems are those
(All the usual ones? Or is it a rare feature?)
* I miss a complete specification of what facets and other pieces of info
a policy can/must have, what happens when some particular facet/piece
of info is not explicitly provided, and what facets/pieces of info can be
user-defined instead of simply chosen from the available catalog of options
in the library.
* The reference includes lots of small classes such as type_id,
vptr_type, etc.
but itâs not clear to me if these are implementation details or things for
which a user can provide alternative implementations within a policy. If the
former, Iâd remove them from the reference.
* The section âCore APIâ explains how to implement the âpokeâ open method
without utility macros. But then the example uses âpoke::fn(â¦)â rather than
plain âpoke(â¦)â. What additional step is needed to achieve âpoke(â¦)â syntax
as macro-supported examples accomplish? (Peeking into the code, I see that
BOOST_OPEN_METHOD includes the definition of a free-standing forwarding
function inline auto NAME(ForwarderParameters&&... args)â¦)
* Core API: the example uses BOOST_OPENMETHOD_REGISTER, it would be
nice to show how to get rid of all macros, including this one.
* A virtual argument in an open method can be defined with virtual_ptr<C> or
virtual_<C>. I understand why these two exist--the former is more efficient.
But then we also have virtual_shared_ptr and virtual_unique_ptr: whatâs the
advantage of using virtual_shared_ptr<C> instead of
std::shared_ptr<virtual_ptr<C>>? This seems to add some clutter to the
lib for
no real benefit, plus it conflates the concepts of âvirtual slotâ and
âobject lifetimeâ, which in my mind are completely orthogonal, the
latter not
really belonging in this library, much like classical virtual functions
are not
concerned with object lifetime either. FWIW, this extension is not part
of N2216.
* Elaborating on the previous point, a cleaner approach (IMHO) would be to
mark virtual slots with virtual_<C> exclusively, and then let this
interoperate
smoothly with virtual_ptr <C> (or virtual_ref if the name is finally
changed).
That is, virtual_ is concerned with the signature of open methods only, and
virtual_ptr is concerned with run-time optimization of reference passing,
only.
* I understand that BOOST_OPENMETHOD_DECLARE_OVERRIDER and
BOOST_OPENMETHOD_DEFINE_OVERRIDER separate the declaration and
definition that are covered together in BOOST_OPENMETHOD_OVERRIDE.
But what does BOOST_OPENMETHOD_OVERRIDER do? The explanation in
the reference is quite opaque to me.
* The docs talk about so-called guide functions, but I canât seem to
find this
concept defined anywhere.
* Regarding <boost/openmethod.hpp>, itâs stated that it â[a]lso imports
boost::openmethod::virtual_ptr in the global namespace. This is usually
regarded as bad practice. The rationale is that OpenMethod emulates a
language feature, and virtual_ptr is equivalent to keyword, similar to
virtual.â I agree this is bad practice, Iâd recommend against it.
* I can define and override an open method inside a namespace (say open
method run my_namespace), so thatâs ok. But I canât override outside the
namespace with BOOST_OPENMETHOD_OVERRIDE(my_namespace::run,â¦).
At least, this should be mentioned as a limitation of the macro-based
interface.
* I know that an internal type_hashed is required because of previous
communications with the author around this very point, but I have the
feeling
this piece of the lib is not adequately explained in the docs and a casual
reader may be confused by it.
Joaquin M Lopez Munoz
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk