Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ tmpinst/

CMakeFiles/
build/

doc/html/*
doc/_/*
cov-int/**
9 changes: 8 additions & 1 deletion include/boost/openmethod/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,12 +525,19 @@ constexpr bool has_vptr_fn = std::is_same_v<
std::declval<const Class&>(), std::declval<Registry*>())),
vptr_type>;

BOOST_OPENMETHOD_DETAIL_HAS_STATIC_FN(dynamic_vptr);

template<class Registry, class ArgType>
decltype(auto) acquire_vptr(const ArgType& arg) {
Registry::require_initialized();

if constexpr (detail::has_vptr_fn<ArgType, Registry>) {
if constexpr (has_vptr_fn<ArgType, Registry>) {
return boost_openmethod_vptr(arg, static_cast<Registry*>(nullptr));
} else if constexpr (has_dynamic_vptr<
virtual_traits<const ArgType&, Registry>,
type_id>) {
return virtual_traits<const ArgType&, Registry>::dynamic_vptr(
arg);
} else {
return Registry::template policy<policies::vptr>::dynamic_vptr(arg);
Comment on lines +534 to 542
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_dynamic_vptr is being instantiated with type_id, but virtual_traits<...>::dynamic_vptr (as introduced for std::any) takes the argument type (e.g. const std::any&). As written, the trait will evaluate to false even when dynamic_vptr(const ArgType&) exists, so acquire_vptr falls back to the vptr policy path and breaks custom virtual_traits-based dispatch (likely causing compilation failures for std::any). Update the detection to test dynamic_vptr(std::declval<const ArgType&>()) (or equivalent) so the virtual_traits hook is actually used.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

}
Expand Down
196 changes: 196 additions & 0 deletions include/boost/openmethod/interop/std_any.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
// Copyright (c) 2018-2026 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_OPENMETHOD_INTEROP_STD_ANY_HPP
#define BOOST_OPENMETHOD_INTEROP_STD_ANY_HPP

#include <any>
#include <boost/openmethod/core.hpp>

namespace boost::openmethod {

namespace detail {
template<class Registry>
struct validate_method_parameter<virtual_<std::any>, Registry, void>
: std::true_type {};

template<class Registry>
struct validate_method_parameter<virtual_<const std::any&>, Registry, void>
: std::true_type {};

template<class Registry>
struct validate_method_parameter<virtual_<std::any&>, Registry, void>
: std::true_type {};
Comment on lines +19 to +25
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This header declares validate_method_parameter support for virtual_<std::any&>, but there is no corresponding virtual_traits<std::any&, Registry> specialization. As a result, methods declared with virtual_<std::any&> will still fail to compile / dispatch correctly. Either add a virtual_traits<std::any&, Registry> specialization (with cast(std::any&) so std::any_cast<T&> is possible) or drop the validate_method_parameter<virtual_<std::any&>> specialization to avoid advertising unsupported usage.

Copilot uses AI. Check for mistakes.

template<class Registry>
struct validate_method_parameter<virtual_<std::any&&>, Registry, void>
: std::true_type {};

} // namespace detail

//! Specialize virtual_traits for std::any by value.
//!
//! Dispatch is based on the runtime type of the value stored in the `any`,
//! obtained via `std::any::type()`. Requires the registry to use a @ref
//! rtti policy that provides `dynamic_type` (e.g. @ref std_rtti).
//!
//! @tparam Registry A @ref registry.
template<class Registry>
struct virtual_traits<std::any, Registry> {
//! The type used for dispatch.
using virtual_type = std::any;

//! Returns a const reference to the `any` argument.
//! @param arg A reference to a `std::any`.
//! @return A const reference to `arg`.
static auto peek(const std::any& arg) -> const std::any& {
return arg;
}

//! Returns a *reference* to a v-table pointer for an object.
//!
//! Acquires the dynamic @ref type_id of `arg`, using the registry's
//! @ref rtti policy.
//!
//! If the registry has a @ref type_hash policy, uses it to convert the
//! type id to an index; otherwise, uses the type_id as the index.
//!
//! If the registry contains the @ref runtime_checks policy, verifies
//! that the index falls within the limits of the vector. If it does
//! not, and if the registry contains a @ref error_handler policy, calls
//! its @ref error function with a @ref missing_class value, then
//! terminates the program with @ref abort.
//!
//! @param arg A reference to a const `any`.
//! @return A reference to a the v-table pointer for `Class`.
static auto dynamic_vptr(const std::any& arg) -> const vptr_type& {
return Registry::rtti::type_vptr(arg.type());
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registry::rtti::type_vptr is called here, but the RTTI policy (Registry::rtti) does not define type_vptr (the new type_vptr helper lives on the vptr policy, e.g. Registry::vptr::type_vptr). Also arg.type() is a const std::type_info&, while this library’s type_id for std_rtti is &typeid(T) (a pointer). This currently won’t compile; convert to a type_id (e.g. &arg.type()) and route through the vptr policy’s type_vptr.

Suggested change
return Registry::rtti::type_vptr(arg.type());
return Registry::vptr::type_vptr(&arg.type());

Copilot uses AI. Check for mistakes.
};

//! Cast to a type.
//!
//! Extracts the stored value using `std::any_cast`.
//!
//! @tparam U The target type (e.g. `Dog&`, `const Dog&`, `Dog`).
//! @param arg An rvalue reference to the `std::any` method argument.
//! @return The value stored in `arg`, cast to `U`.
template<typename U>
static auto cast(std::any&& arg) -> decltype(auto) {
return std::any_cast<U>(arg);
}
};

//! Specialize virtual_traits for `std::any&` (mutable reference).
//!
//! @tparam Registry A @ref registry.
template<class Registry>
struct virtual_traits<const std::any&, Registry> {
//! The type used for dispatch.
Comment on lines +85 to +90
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc says this is for std::any& (mutable reference), but the actual specialization is virtual_traits<const std::any&, Registry>. Also, with a const std::any& input, std::any_cast<T&> is not permitted, so the comment about supporting mutable references is misleading. Please update the specialization/signature (if mutability is intended) or adjust the documentation to reflect const-only behavior.

Copilot uses AI. Check for mistakes.
using virtual_type = std::any;

//! Returns a const reference to the `any` argument.
//! @param arg A reference to a `std::any`.
//! @return A const reference to `arg`.
static auto peek(const std::any& arg) -> const std::any& {
return arg;
}

//! Returns a *reference* to a v-table pointer for an object.
//!
//! Acquires the dynamic @ref type_id of `arg`, using the registry's
//! @ref rtti policy.
//!
//! If the registry has a @ref type_hash policy, uses it to convert the
//! type id to an index; otherwise, uses the type_id as the index.
//!
//! If the registry contains the @ref runtime_checks policy, verifies
//! that the index falls within the limits of the vector. If it does
//! not, and if the registry contains a @ref error_handler policy, calls
//! its @ref error function with a @ref missing_class value, then
//! terminates the program with @ref abort.
//!
//! @param arg A reference to a const `any`.
//! @return A reference to a the v-table pointer for `Class`.
static auto dynamic_vptr(const std::any& arg) -> const vptr_type& {
return Registry::vptr::type_vptr(&arg.type());
};

//! Cast to a type.
//!
//! Extracts the stored value using `std::any_cast`. Supports mutable
//! references (e.g. `Dog&`) because the `any` argument is non-const.
//!
//! @tparam U The target type (e.g. `Dog&`, `const Dog&`, `Dog`).
//! @param arg A mutable reference to the `std::any` method argument.
//! @return The value stored in `arg`, cast to `U`.
template<typename U>
static auto cast(const std::any& arg) -> decltype(auto) {
return std::any_cast<U>(arg);
}
};

//! Specialize virtual_traits for std::any by value.
//!
//! Dispatch is based on the runtime type of the value stored in the `any`,
//! obtained via `std::any::type()`. Requires the registry to use a @ref
//! rtti policy that provides `dynamic_type` (e.g. @ref std_rtti).
//!
//! @tparam Registry A @ref registry.
template<class Registry>
struct virtual_traits<std::any&&, Registry> {
//! The type used for dispatch.
using virtual_type = std::any;

//! Returns a const reference to the `any` argument.
//! @param arg A reference to a `std::any`.
//! @return A const reference to `arg`.
static auto peek(const std::any& arg) -> const std::any& {
return arg;
}

//! Returns a *reference* to a v-table pointer for an object.
//!
//! Acquires the dynamic @ref type_id of `arg`, using the registry's
//! @ref rtti policy.
//!
//! If the registry has a @ref type_hash policy, uses it to convert the
//! type id to an index; otherwise, uses the type_id as the index.
//!
//! If the registry contains the @ref runtime_checks policy, verifies
//! that the index falls within the limits of the vector. If it does
//! not, and if the registry contains a @ref error_handler policy, calls
//! its @ref error function with a @ref missing_class value, then
//! terminates the program with @ref abort.
//!
//! @param arg A reference to a const `any`.
//! @return A reference to a the v-table pointer for `Class`.
static auto dynamic_vptr(const std::any& arg) -> const vptr_type& {
return Registry::rtti::type_vptr(arg.type());
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as the std::any by-value specialization above: Registry::rtti doesn’t provide type_vptr, and arg.type() isn’t a type_id. This should dispatch via the vptr policy (Registry::vptr::type_vptr) using a type_id derived from &arg.type() (for the std_rtti type_id representation).

Suggested change
return Registry::rtti::type_vptr(arg.type());
return Registry::vptr::type_vptr(&arg.type());

Copilot uses AI. Check for mistakes.
};

//! Cast to a type.
//!
//! Extracts the stored value using `std::any_cast`.
//!
//! @tparam U The target type (e.g. `Dog&`, `const Dog&`, `Dog`).
//! @param arg An rvalue reference to the `std::any` method argument.
//! @return The value stored in `arg`, cast to `U`.
template<typename U>
static auto cast(std::any&& arg) -> decltype(auto) {
return std::any_cast<U>(arg);
}
};

template<typename... T>
struct use_any_types : detail::use_class_aux<
typename detail::extract_registry<T...>::registry,
mp11::mp_list<std::any>>,
detail::use_class_aux<
typename detail::extract_registry<T...>::registry,
mp11::mp_list<T, std::any>>... {};

} // namespace boost::openmethod

#endif
16 changes: 15 additions & 1 deletion include/boost/openmethod/policies/vptr_map.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,21 @@ class vptr_map : public vptr {
//! @return A reference to a the v-table pointer for `Class`.
template<class Class>
static auto dynamic_vptr(const Class& arg) -> const vptr_type& {
auto type = Registry::rtti::dynamic_type(arg);
return type_vptr(Registry::rtti::dynamic_type(arg));
}

//! Returns a *reference* to a v-table pointer for a type.
//!
//! If the registry contains the @ref runtime_checks policy, checks that
//! the map contains the type id. If it does not, and if the registry
//! contains a @ref error_handler policy, calls its
//! @ref error function with a @ref missing_class value, then
//! terminates the program with @ref abort.
//!
//! @tparam Class A registered class.
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type_vptr is not a function template, but its doc block includes @tparam Class A registered class.. Please drop that @tparam line (or convert type_vptr into a template if that was intended) so the documentation matches the signature.

Suggested change
//! @tparam Class A registered class.

Copilot uses AI. Check for mistakes.
//! @param type A `type_id`.
//! @return A reference to a the v-table pointer for `type`.
static auto type_vptr(type_id type) -> const vptr_type& {
auto iter = vptrs.find(type);

if constexpr (Registry::has_runtime_checks) {
Expand Down
25 changes: 21 additions & 4 deletions include/boost/openmethod/policies/vptr_vector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,29 @@ struct vptr_vector : vptr {
//! @return A reference to a the v-table pointer for `Class`.
template<class Class>
static auto dynamic_vptr(const Class& arg) -> const vptr_type& {
auto dynamic_type = Registry::rtti::dynamic_type(arg);
return type_vptr(Registry::rtti::dynamic_type(arg));
};

//! Returns a *reference* to a v-table pointer for a type.
//!
//! If the registry has a @ref type_hash policy, uses it to convert the
//! type id to an index; otherwise, uses the type_id as the index.
//!
//! If the registry contains the @ref runtime_checks policy, verifies
//! that the index falls within the limits of the vector. If it does
//! not, and if the registry contains a @ref error_handler policy, calls
//! its @ref error function with a @ref missing_class value, then
//! terminates the program with @ref abort.
//!
//! @tparam Class A registered class.
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type_vptr is not a function template, but its doc block still lists @tparam Class A registered class. (copied from dynamic_vptr). Consider removing that @tparam line to avoid confusing generated docs.

Suggested change
//! @tparam Class A registered class.

Copilot uses AI. Check for mistakes.
//! @param type A `type_id`.
//! @return A reference to a the v-table pointer for `type`.
static auto type_vptr(type_id type) -> const vptr_type& {
std::size_t index;
if constexpr (has_type_hash) {
index = type_hash::hash(dynamic_type);
index = type_hash::hash(type);
} else {
index = std::size_t(dynamic_type);
index = std::size_t(type);

if constexpr (Registry::has_runtime_checks) {
std::size_t max_index = 0;
Expand All @@ -153,7 +170,7 @@ struct vptr_vector : vptr {
if (index >= max_index) {
if constexpr (Registry::has_error_handler) {
missing_class error;
error.type = dynamic_type;
error.type = type;
Registry::error_handler::error(error);
}

Expand Down
12 changes: 12 additions & 0 deletions include/boost/openmethod/preamble.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/bind.hpp>
#include <boost/preprocessor/cat.hpp>

#include <stdlib.h>
#include <vector>
Expand Down Expand Up @@ -869,6 +870,17 @@ struct initialize_aux;

} // namespace detail

#define BOOST_OPENMETHOD_DETAIL_HAS_STATIC_FN(FN) \
template<typename, class, class...> \
struct BOOST_PP_CAT(has_, BOOST_PP_CAT(FN, _aux)) : std::false_type {}; \
template<class T, class... Args> \
struct BOOST_PP_CAT(has_, BOOST_PP_CAT(FN, _aux))< \
std::void_t<decltype(T::FN(std::declval<Args>()...))>, T, Args...> \
: std::true_type {}; \
template<class T, class... Args> \
constexpr bool BOOST_PP_CAT(has_, FN) = \
BOOST_PP_CAT(has_, BOOST_PP_CAT(FN, _aux))<void, T, Args...>::value

//! Methods, classes and policies.
//!
//! Methods exist in the context of a registry. Any class used as a method or
Expand Down
Loading
Loading