Skip to content

Commit 57b6c32

Browse files
jll63claude
andcommitted
add virtual_any
virtual_any<Any, Registry> is to `any` what virtual_ptr is to a pointer: it combines an `any` - held by value - with the v-table pointer for the contained value, so methods dispatch on the contained type without looking it up on every call. The v-table pointer is acquired at construction: from the dynamic type of an existing `any` (a hash table lookup via virtual_traits<const Any&>::vptr), or statically when the contained type is known (the value constructor, emplace, and the make_*_virtual factories use static_vptr, like make_unique_virtual). Assignment and emplace re-derive it, and no mutable accessor to the `any` is exposed, so the vptr always matches the payload. Methods take virtual_any by const, mutable or rvalue reference; overriders receive the contained type by a reference of a compatible category - the casts delegate to the existing virtual_traits<Any cvref> specializations - or the virtual_any itself, unchanged, for a catch-all overrider. Passing virtual_any by value is rejected: it would copy the payload on every call. The value constructor makes overrider parameters convertible to the method's, so BOOST_OPENMETHOD_OVERRIDE locates virtual_any methods; the mutable lvalue case still needs method<...>::override<Fn>, as with virtual_<Any&>. No changes to core.hpp: dispatch reads the stored vptr through the boost_openmethod_vptr hook (a friend, so ADL only finds it when a virtual_any is an argument), and the detail templates (is_virtual, parameter_traits, validate_method_parameter, validate_overrider_parameter, select_overrider_virtual_type_aux) are specialized on the concrete class. The exact-pair validate_overrider_parameter specializations disambiguate with the generic <T, T> one, which partial ordering ranks neither above nor below <virtual_any cvref, T2>. The class is generic: it only requires virtual_traits<Any cvref> with vptr and cast, so it serves std::any, boost::any, and future any-likes. std_any.hpp and boost_any.hpp provide the default-registry aliases virtual_std_any and virtual_boost_any and the make_std_any_virtual and make_boost_any_virtual factories. They also delete the final_virtual_ptr overloads for their `any` type: the primary template would silently use static_vptr<any> - the v-table of the `any` root class, not of the contained value. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 65cd1b5 commit 57b6c32

9 files changed

Lines changed: 1173 additions & 2 deletions

File tree

doc/modules/ROOT/pages/ref_headers.adoc

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,21 +71,43 @@ Provides a `virtual_traits` specialization that makes it possible to use a
7171
Provides a `virtual_traits` specialization that makes it possible to use a
7272
`boost::intrusive_ptr` in place of a raw pointer or reference in virtual parameters.
7373

74+
[#virtual_any]
75+
### link:{{BASE_URL}}/include/boost/openmethod/interop/virtual_any.hpp[<boost/openmethod/interop/virtual_any.hpp>]
76+
77+
Provides `virtual_any`, a wide `any` that combines an `any`, held by value,
78+
with a pointer to the v-table for the contained value - like `virtual_ptr`
79+
combines a pointer to an object with a pointer to its v-table. The v-table
80+
pointer is acquired when the `virtual_any` is created, so methods dispatch on
81+
the contained type without looking it up on every call. Also provides
82+
`make_any_virtual`, which creates a `virtual_any` containing a value of a
83+
statically known type, setting the v-table pointer without any lookup. This
84+
header is included by `std_any.hpp` and `boost_any.hpp`; it can serve any type
85+
with an `any`-like interface, given `virtual_traits` specializations for its
86+
reference types.
87+
7488
[#std_any]
7589
### link:{{BASE_URL}}/include/boost/openmethod/interop/std_any.hpp[<boost/openmethod/interop/std_any.hpp>]
7690

7791
Provides `virtual_traits` specializations that make it possible to use a `std::any` -
7892
by const reference, by mutable reference, or by rvalue reference - in virtual
7993
parameters. Dispatch is on the type of the contained value. Also provides
80-
`use_std_any_types`, which registers the types that may be contained.
94+
`use_std_any_types`, which registers the types that may be contained;
95+
`virtual_std_any`, an alias for `virtual_any<std::any>`, and
96+
`make_std_any_virtual`. In addition, the header deletes the
97+
`final_virtual_ptr` overloads for `std::any`, which would otherwise silently
98+
use the v-table of the `any` root class instead of the contained value's.
8199

82100
[#boost_any]
83101
### link:{{BASE_URL}}/include/boost/openmethod/interop/boost_any.hpp[<boost/openmethod/interop/boost_any.hpp>]
84102

85103
Provides `virtual_traits` specializations that make it possible to use a `boost::any` -
86104
by const reference, by mutable reference, or by rvalue reference - in virtual
87105
parameters. Dispatch is on the type of the contained value. Also provides
88-
`use_boost_any_types`, which registers the types that may be contained.
106+
`use_boost_any_types`, which registers the types that may be contained;
107+
`virtual_boost_any`, an alias for `virtual_any<boost::any>`, and
108+
`make_boost_any_virtual`. In addition, the header deletes the
109+
`final_virtual_ptr` overloads for `boost::any`, which would otherwise silently
110+
use the v-table of the `any` root class instead of the contained value's.
89111

90112
*The headers below are for advanced use*.
91113

include/boost/openmethod/interop/boost_any.hpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <boost/any.hpp>
1010
#include <boost/openmethod/core.hpp>
11+
#include <boost/openmethod/interop/virtual_any.hpp>
1112

1213
namespace boost::openmethod {
1314

@@ -238,6 +239,57 @@ struct use_boost_any_types
238239
typename detail::extract_registry<T...>::registry,
239240
mp11::mp_list<T, boost::any>>... {};
240241

242+
//! Alias for a `virtual_any<boost::any>`, in the default registry.
243+
//!
244+
//! With another registry, use `virtual_any<boost::any, Registry>` directly.
245+
using virtual_boost_any = virtual_any<boost::any>;
246+
247+
//! Create a new object and return a `virtual_boost_any` containing it.
248+
//!
249+
//! Create a `Class` from `args`, store it in a `boost::any`, and return a
250+
//! @ref virtual_any with its v-table pointer set to the
251+
//! @ref registry::static_vptr for `Class` - no hash table lookup is
252+
//! involved.
253+
//!
254+
//! @tparam Class The type of the value to create.
255+
//! @tparam Registry A @ref registry.
256+
//! @tparam T Types of the arguments to pass to the constructor of
257+
//! `Class`.
258+
//! @param args Arguments to pass to the constructor of `Class`.
259+
//! @return A `virtual_any<boost::any, Registry>` containing a newly created
260+
//! `Class`.
261+
template<
262+
class Class, class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY,
263+
typename... T>
264+
inline auto
265+
make_boost_any_virtual(T&&... args) -> virtual_any<boost::any, Registry> {
266+
return make_any_virtual<Class, boost::any, Registry>(
267+
std::forward<T>(args)...);
268+
}
269+
270+
// The primary final_virtual_ptr would silently use static_vptr<boost::any>
271+
// - the v-table of the `any` root class, not of the contained value.
272+
// Delete the combination. Both call forms need covering: the non-template
273+
// overloads catch calls that deduce the default registry, and are removed
274+
// from consideration when an explicit template argument list is given, so
275+
// the Registry-only templates - more specialized than the primary - catch
276+
// those.
277+
278+
template<class Registry>
279+
void final_virtual_ptr(const boost::any&) = delete;
280+
template<class Registry>
281+
void final_virtual_ptr(boost::any&) = delete;
282+
template<class Registry>
283+
void final_virtual_ptr(boost::any&&) = delete;
284+
void final_virtual_ptr(const boost::any&) = delete;
285+
void final_virtual_ptr(boost::any&) = delete;
286+
void final_virtual_ptr(boost::any&&) = delete;
287+
288+
namespace aliases {
289+
using boost::openmethod::make_boost_any_virtual;
290+
using boost::openmethod::virtual_boost_any;
291+
} // namespace aliases
292+
241293
} // namespace boost::openmethod
242294

243295
#endif

include/boost/openmethod/interop/std_any.hpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <any>
1010
#include <boost/openmethod/core.hpp>
11+
#include <boost/openmethod/interop/virtual_any.hpp>
1112

1213
namespace boost::openmethod {
1314

@@ -201,6 +202,57 @@ struct use_std_any_types
201202
typename detail::extract_registry<T...>::registry,
202203
mp11::mp_list<T, std::any>>... {};
203204

205+
//! Alias for a `virtual_any<std::any>`, in the default registry.
206+
//!
207+
//! With another registry, use `virtual_any<std::any, Registry>` directly.
208+
using virtual_std_any = virtual_any<std::any>;
209+
210+
//! Create a new object and return a `virtual_std_any` containing it.
211+
//!
212+
//! Create a `Class` from `args`, store it in a `std::any`, and return a
213+
//! @ref virtual_any with its v-table pointer set to the
214+
//! @ref registry::static_vptr for `Class` - no hash table lookup is
215+
//! involved.
216+
//!
217+
//! @tparam Class The type of the value to create.
218+
//! @tparam Registry A @ref registry.
219+
//! @tparam T Types of the arguments to pass to the constructor of
220+
//! `Class`.
221+
//! @param args Arguments to pass to the constructor of `Class`.
222+
//! @return A `virtual_any<std::any, Registry>` containing a newly created
223+
//! `Class`.
224+
template<
225+
class Class, class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY,
226+
typename... T>
227+
inline auto
228+
make_std_any_virtual(T&&... args) -> virtual_any<std::any, Registry> {
229+
return make_any_virtual<Class, std::any, Registry>(
230+
std::forward<T>(args)...);
231+
}
232+
233+
// The primary final_virtual_ptr would silently use static_vptr<std::any>
234+
// - the v-table of the `any` root class, not of the contained value.
235+
// Delete the combination. Both call forms need covering: the non-template
236+
// overloads catch calls that deduce the default registry, and are removed
237+
// from consideration when an explicit template argument list is given, so
238+
// the Registry-only templates - more specialized than the primary - catch
239+
// those.
240+
241+
template<class Registry>
242+
void final_virtual_ptr(const std::any&) = delete;
243+
template<class Registry>
244+
void final_virtual_ptr(std::any&) = delete;
245+
template<class Registry>
246+
void final_virtual_ptr(std::any&&) = delete;
247+
void final_virtual_ptr(const std::any&) = delete;
248+
void final_virtual_ptr(std::any&) = delete;
249+
void final_virtual_ptr(std::any&&) = delete;
250+
251+
namespace aliases {
252+
using boost::openmethod::make_std_any_virtual;
253+
using boost::openmethod::virtual_std_any;
254+
} // namespace aliases
255+
204256
} // namespace boost::openmethod
205257

206258
#endif

0 commit comments

Comments
 (0)