Boost logo

Boost :

From: Jean-Louis Leroy (jl_at_[hidden])
Date: 2025-05-06 14:28:38


I replied privately instead of posting to the list. Sorry, very much under the
weather ATM. Re-posting to the list, with one extra remark.

> Another question concerning the memory footprint.
>
> Suppose we have a class hierarchy of size N so that:
> struct A1 {...}
> struct A2 : A1 {...}
> ...
> struct AN : AN-1 {...}
>
> Now we define an open method with M virtual arguments BOOST_OPENMETHOD(big,
> (virtual_ptr<A1>, virtual_ptr<A1>, virtual_ptr<A1>, ..., virtual_ptr<A1>),
> void);
>
> What set of overriders would be the worst case in terms of memory footprint ?
> I suppose it would be the complete set of overriders that covers all the
> tuples possibilty, for which size is N^M. Then would the size of the dispatch
> table be O(N^M) ?

> What set of overriders would be the worst case in terms of memory footprint ?
> I suppose it would be the complete set of overriders that covers all the
> tuples possibilty, for which size is N^M.

If each virtual tuple gets its own overrider? In that case, indeed, the dispatch
table will be N^M. And that wouldn't even be bad. It would just be what it
takes. Any solution in the tune of switches or M-dispatch (and good luck
figuring it out, you'd have to generate it) would cost more in terms of code
space than it saves in terms of data space. Unless you start playing tricks like
putting the shortest possible integers in the dispatch table, indexing a vector
of function pointers.

Otherwise, it depends. In the extreme case where there is only one overrider for
the fallback case (all virtual_ptr<A1>s), the table will have just one cell.

The worst, I think, would be overriders on the diagonal only. Maximum waste.

On Tue, May 6, 2025 at 1:06 AM Yannick Le Goc <ylegoc_at_[hidden]> wrote:
>
> Another question concerning the memory footprint.
>
> Suppose we have a class hierarchy of size N so that:
> struct A1 {...}
> struct A2 : A1 {...}
> ...
> struct AN : AN-1 {...}
>
> Now we define an open method with M virtual arguments BOOST_OPENMETHOD(big, (virtual_ptr<A1>, virtual_ptr<A1>, virtual_ptr<A1>, ..., virtual_ptr<A1>), void);
>
> What set of overriders would be the worst case in terms of memory footprint ?
> I suppose it would be the complete set of overriders that covers all the tuples possibilty, for which size is N^M.
> Then would the size of the dispatch table be O(N^M) ?
>
> On Mon, May 5, 2025 at 3:11 AM Jean-Louis Leroy via Boost <boost_at_[hidden]> wrote:
>>
>> > First, thanks for all the effort you put into writing a solid library on an
>> > experimental subject.
>>
>> Hi Yannick! Thank you for the feedback.
>>
>> > 1. Concerning the overrider selection, when the perfect match is not found, it
>> > is said that an arbitrary choice is made.
>>
>> Everybody seems to dislike this, including myself. It is the behavior specified
>> in N2216.
>>
>> > My tests on virtual inheritance and two dimensions dispatch showed that this
>> > is the last defined compatible overrider that is chosen. Do you confirm ?
>>
>> I don't. It's a secret ;-) Only I can use that knowledge in my unit tests.
>>
>> The only guarantee is that the same overrider is always picked for the same set
>> of virtual arguments for the duration of the process.
>>
>> > 2. The class virtual_ptr<Class> accepts only classes that inherit Class. Would
>> > it be possible to accept inheritance unrelated classes e.g.
>> > std::filesystem::path and std::string ?
>>
>> Not directly. What would the method signature, and the method call look like?
>> You could wrap them in a class hierarchy, e.g. AbstractPath, FileSystemPath and
>> StringPath. I have some ideas about supporting std::any virtual
>> parameters though,
>> where the overriders would specify an exact type, something like:
>>
>> BOOST_OPENMETHOD(delete, (virtual_<std::any> path), void);
>>
>> BOOST_OPENMETHOD_OVERRIDE(delete, (std::filesystem::path path), void) {
>> ...
>> }
>>
>> BOOST_OPENMETHOD_OVERRIDE(delete, (std::string path), void) {
>> ...
>> }
>>
>> > 3. Would it be possible to get rid of the class declarations ? Indeed they are
>> > already declared in the template parameter of the virtual_ptr<> arguments
>> > so they could be implicitly defined here.
>>
>> Ah but you are forgetting about intermediate classes in the hierarchy, between
>> the method and the overrider:
>>
>> struct A { ... };
>> struct I : A { ... };
>> struct B : I { ... };
>>
>> BOOST_OPENMETHOD(whatever, (virtual_ptr<A>), void);
>>
>> BOOST_OPENMETHOD_OVERRIDE(whatever, (virtual_ptr<B>), void) {
>> ...
>> }
>>
>> auto p = std::make_unique<I>();
>> whatever(*p);
>>
>> That being said, with_vptr must be called at every level of the hierarchy, and
>> indeed, it does class registration for you.
>>
>> At some point in the future, reflection might be good enough to help with this.
>>
>> J-L
>>
>> On Sun, May 4, 2025 at 4:11 PM Yannick Le Goc <ylegoc_at_[hidden]> wrote:
>> >
>> > Hi Jean-Louis,
>> >
>> > First, thanks for all the effort you put into writing a solid library on an experimental subject.
>> > I have a few questions.
>> >
>> > 1. Concerning the overrider selection, when the perfect match is not found, it is said that an arbitrary choice is made.
>> > My tests on virtual inheritance and two dimensions dispatch showed that this is the last defined compatible overrider that is chosen.
>> > Do you confirm ? Or what is the algorithm ?
>> >
>> > 2. The class virtual_ptr<Class> accepts only classes that inherit Class. Would it be possible to accept inheritance unrelated classes e.g. std::filesystem::path and std::string ?
>> >
>> > 3. Would it be possible to get rid of the class declarations ? Indeed they are already declared in the template parameter of the virtual_ptr<> arguments so they could be implicitly defined here.
>> >
>> > Yannick
>> >
>> >
>> > On Sun, May 4, 2025 at 11:15 AM Jean-Louis Leroy via Boost <boost_at_[hidden]> wrote:
>> >>
>> >> > struct dynamic_policy
>> >> > : boost::openmethod::default_policy::fork<dynamic_policy>::replace<
>> >> > boost::openmethod::policies::extern_vptr,
>> >> > boost::openmethod::policies::vptr_vector<
>> >> > dynamic_policy,
>> >> > boost::openmethod::policies::indirect_vptr>> {};
>> >> >
>> >> > Committing this code to any project I've worked on would've left most of my
>> >> > coworkers absolutely checked out.
>> >>
>> >> With the changes in the review branch, you can now say:
>> >>
>> >> struct dynamic_policy
>> >> : boost::openmethod::default_policy::fork<dynamic_policy>::add<
>> >> boost::openmethod::policies::indirect_vptr>> {};
>> >>
>> >> I am pretty sure I can make this work:
>> >>
>> >> struct map_policy
>> >> : default_policy::fork<map_policy>::with<vptr_map<map_policy>> {};
>> >>
>> >> I.e. instead of saying `replace<Facet1, Implementation1>::replace<Facet2,
>> >> Implementation2>` you can just say `with<Implementation1, Implementation2>`. If
>> >> an implementation of the same facet exists in the policy, it will be replaced
>> >> with the new one, otherwise the implementation will be added. Not in the review
>> >> branch yet.
>> >>
>> >> _______________________________________________
>> >> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
>>
>> _______________________________________________
>> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost


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