4 Commits

Author SHA1 Message Date
Jean-Louis Leroy
55b8c07a57 disable incorrect coverity warnings 2026-01-20 16:50:17 -05:00
Jean-Louis Leroy
6fdbfb361a reformat 2026-01-20 16:50:17 -05:00
Jean-Louis Leroy
f0a8656744 interop with boost::intrusive_ptr 2026-01-19 21:31:19 -05:00
Jean-Louis Leroy
c465260fe7 shared_ptr: avoid temporary in 'cast' if source and target types are the same 2026-01-18 11:58:36 -05:00
15 changed files with 1063 additions and 102 deletions

View File

@@ -92,21 +92,30 @@ set(
Boost::preprocessor
)
if (BOOST_OPENMETHOD_BUILD_TESTS OR BOOST_OPENMETHOD_MRDOCS_BUILD)
list(APPEND BOOST_OPENMETHOD_DEPENDENCIES Boost::smart_ptr)
endif()
foreach (BOOST_OPENMETHOD_DEPENDENCY ${BOOST_OPENMETHOD_DEPENDENCIES})
if (BOOST_OPENMETHOD_DEPENDENCY MATCHES "^[ ]*Boost::([A-Za-z0-9_]+)[ ]*$")
list(APPEND BOOST_OPENMETHOD_INCLUDE_LIBRARIES ${CMAKE_MATCH_1})
endif ()
endforeach ()
# Conditional dependencies
if (BOOST_OPENMETHOD_MRDOCS_BUILD)
list(APPEND BOOST_OPENMETHOD_INCLUDE_LIBRARIES smart_ptr)
endif()
if (NOT BOOST_OPENMETHOD_MRDOCS_BUILD)
if (BUILD_TESTING OR BOOST_OPENMETHOD_BUILD_TESTS)
if (BOOST_OPENMETHOD_BUILD_TESTS)
set(BOOST_OPENMETHOD_UNIT_TEST_LIBRARIES test)
if (BOOST_OPENMETHOD_BUILD_EXAMPLES)
set(BOOST_OPENMETHOD_EXAMPLE_LIBRARIES dll)
endif()
endif()
endif()
# Complete dependency list
set(BOOST_INCLUDE_LIBRARIES ${BOOST_OPENMETHOD_INCLUDE_LIBRARIES} ${BOOST_OPENMETHOD_UNIT_TEST_LIBRARIES} ${BOOST_OPENMETHOD_EXAMPLE_LIBRARIES})
set(BOOST_EXCLUDE_LIBRARIES openmethod)

View File

@@ -3,146 +3,153 @@
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
// https://godbolt.org/z/r6o4f171r
#include <iostream>
#include <memory>
#include <string>
#include <typeinfo>
#include <boost/openmethod.hpp>
#include <boost/openmethod/policies/static_rtti.hpp>
#include <boost/openmethod/initialize.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>
using std::make_shared;
using std::shared_ptr;
using std::string;
using boost::openmethod::make_shared_virtual;
using boost::openmethod::shared_virtual_ptr;
using boost::openmethod::virtual_ptr;
using namespace boost::openmethod::aliases;
struct matrix {
virtual ~matrix() {
}
virtual auto at(int row, int col) const -> double = 0;
// ...
struct abstract {
int ref_count = 0;
};
struct dense_matrix : matrix {
virtual auto at(int /*row*/, int /*col*/) const -> double {
return 0;
struct registry
: boost::openmethod::registry<boost::openmethod::policies::static_rtti> {};
template<class Rep>
using matrix_ptr = boost::openmethod::virtual_ptr<Rep, registry>;
BOOST_OPENMETHOD(destroy, (matrix_ptr<abstract>), void, registry);
class matrix {
matrix_ptr<abstract> rep_;
explicit matrix(matrix_ptr<abstract> rep) : rep_(rep) {
++rep_->ref_count;
if (--rep->ref_count == 0) {
destroy(rep);
}
}
public:
matrix(const matrix&) = default;
matrix& operator=(const matrix&) = default;
auto rep() const -> matrix_ptr<abstract> {
return rep_;
}
template<class Rep, class... Args>
static matrix make(Args&&... args) {
return matrix(
boost::openmethod::final_virtual_ptr<registry>(
*new Rep(std::forward<Args>(args)...)));
}
};
struct diagonal_matrix : matrix {
virtual auto at(int /*row*/, int /*col*/) const -> double {
return 0;
}
struct dense : abstract {
static constexpr const char* type = "dense";
};
BOOST_OPENMETHOD_CLASSES(matrix, dense_matrix, diagonal_matrix);
BOOST_OPENMETHOD(to_json, (virtual_ptr<const matrix>), string);
BOOST_OPENMETHOD_OVERRIDE(to_json, (virtual_ptr<const dense_matrix>), string) {
return "json for dense matrix...";
BOOST_OPENMETHOD_OVERRIDE(destroy, (matrix_ptr<dense> rep), void) {
delete rep.get();
}
BOOST_OPENMETHOD_OVERRIDE(
to_json, (virtual_ptr<const diagonal_matrix>), string) {
return "json for diagonal matrix...";
struct diagonal : abstract {
static constexpr const char* type = "diagonal";
};
BOOST_OPENMETHOD_OVERRIDE(destroy, (matrix_ptr<diagonal> rep), void) {
delete rep.get();
}
BOOST_OPENMETHOD_CLASSES(abstract, dense, diagonal, registry);
// -----------------------------------------------------------------------------
// matrix * matrix
BOOST_OPENMETHOD(
times, (shared_virtual_ptr<const matrix>, shared_virtual_ptr<const matrix>),
shared_virtual_ptr<const matrix>);
times, (matrix_ptr<abstract>, matrix_ptr<abstract>), matrix, registry);
// catch-all matrix * matrix -> dense_matrix
// catch-all matrix * matrix -> dense
BOOST_OPENMETHOD_OVERRIDE(
times,
(shared_virtual_ptr<const matrix> /*a*/,
shared_virtual_ptr<const matrix> /*b*/),
shared_virtual_ptr<const dense_matrix>) {
return make_shared<const dense_matrix>();
times, (matrix_ptr<abstract> /*a*/, matrix_ptr<abstract> /*b*/), matrix) {
return matrix::make<dense>();
}
// diagonal_matrix * diagonal_matrix -> diagonal_matrix
// diagonal * diagonal -> diagonal
BOOST_OPENMETHOD_OVERRIDE(
times,
(shared_virtual_ptr<const diagonal_matrix> /*a*/,
shared_virtual_ptr<const diagonal_matrix> /*b*/),
shared_virtual_ptr<const diagonal_matrix>) {
return make_shared_virtual<diagonal_matrix>();
times, (matrix_ptr<diagonal> /*a*/, matrix_ptr<diagonal> /*b*/), matrix) {
return matrix::make<diagonal>();
}
inline auto operator*(shared_ptr<const matrix> a, shared_ptr<const matrix> b)
-> shared_virtual_ptr<const matrix> {
return times(a, b);
inline auto operator*(matrix a, matrix b) -> matrix {
return times(a.rep(), b.rep());
}
// -----------------------------------------------------------------------------
// scalar * matrix
BOOST_OPENMETHOD(
times, (double, shared_virtual_ptr<const matrix>),
shared_virtual_ptr<const matrix>);
BOOST_OPENMETHOD(times, (double, matrix_ptr<abstract>), matrix, registry);
// catch-all matrix * scalar -> dense_matrix
// catch-all matrix * scalar -> dense
BOOST_OPENMETHOD_OVERRIDE(
times, (double /*a*/, shared_virtual_ptr<const matrix> /*b*/),
shared_virtual_ptr<const dense_matrix>) {
return make_shared_virtual<dense_matrix>();
times, (double /*a*/, matrix_ptr<abstract> /*b*/), matrix) {
return matrix::make<dense>();
}
BOOST_OPENMETHOD_OVERRIDE(
times, (double /*a*/, shared_virtual_ptr<const diagonal_matrix> /*b*/),
shared_virtual_ptr<const diagonal_matrix>) {
return make_shared_virtual<diagonal_matrix>();
times, (double /*a*/, matrix_ptr<diagonal> /*b*/), matrix) {
return matrix::make<diagonal>();
}
// -----------------------------------------------------------------------------
// matrix * scalar
// just swap
inline auto times(shared_virtual_ptr<const matrix> a, double b)
-> shared_virtual_ptr<const matrix> {
inline auto times(matrix_ptr<abstract> a, double b) -> matrix {
return times(b, a);
}
// -----------------------------------------------------------------------------
// main
#define check(expr) \
{ \
if (!(expr)) { \
cerr << #expr << " failed\n"; \
} \
}
BOOST_OPENMETHOD(write, (matrix_ptr<abstract>), string, registry);
inline auto operator<<(std::ostream& os, matrix a) -> std::ostream& {
return os << write(a.rep());
}
BOOST_OPENMETHOD_OVERRIDE(write, (matrix_ptr<dense>), string) {
return "a dense matrix";
}
BOOST_OPENMETHOD_OVERRIDE(write, (matrix_ptr<diagonal>), string) {
return "a diagonal matrix";
}
auto main() -> int {
using std::cerr;
using std::cout;
boost::openmethod::initialize();
boost::openmethod::initialize<registry>();
shared_ptr<const matrix> a = make_shared<dense_matrix>();
shared_ptr<const matrix> b = make_shared<diagonal_matrix>();
matrix a = matrix::make<dense>();
matrix b = matrix::make<diagonal>();
double s = 1;
#ifdef BOOST_CLANG
#pragma clang diagnostic ignored "-Wpotentially-evaluated-expression"
#endif
check(typeid(*times(a, a)) == typeid(dense_matrix));
check(typeid(*times(a, b)) == typeid(dense_matrix));
check(typeid(*times(b, b)) == typeid(diagonal_matrix));
check(typeid(*times(s, a)) == typeid(dense_matrix));
check(typeid(*times(s, b)) == typeid(diagonal_matrix));
cout << to_json(*a) << "\n"; // json for dense matrix
cout << to_json(*b) << "\n"; // json for diagonal matrix
cout << a << "\n";
cout << b << "\n";
return 0;
}

View File

@@ -0,0 +1,150 @@
// Copyright (c) 2018-2025 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)
#include <iostream>
#include <memory>
#include <string>
#include <typeinfo>
#include <boost/openmethod.hpp>
#include <boost/openmethod/initialize.hpp>
#include <boost/openmethod/interop/boost_intrusive_ptr.hpp>
using std::string;
using namespace boost::openmethod::aliases;
struct abstract {
virtual ~abstract() {
}
int ref_count = 0;
friend void intrusive_ptr_add_ref(abstract* p) {
++p->ref_count;
}
friend void intrusive_ptr_release(abstract* p) {
if (--p->ref_count == 0) {
delete p;
}
}
};
template<class Rep>
using matrix_ptr = boost::openmethod::boost_intrusive_virtual_ptr<Rep>;
class matrix {
matrix_ptr<abstract> rep_;
explicit matrix(matrix_ptr<abstract> rep) : rep_(rep) {
}
public:
matrix(const matrix&) = default;
matrix& operator=(const matrix&) = default;
auto rep() const -> matrix_ptr<abstract> {
return rep_;
}
template<class Rep>
static matrix make() {
return matrix(boost::openmethod::make_boost_intrusive_virtual<Rep>());
}
};
struct dense : abstract {};
struct diagonal : abstract {};
using namespace boost::openmethod::aliases;
BOOST_OPENMETHOD_CLASSES(abstract, dense, diagonal);
BOOST_OPENMETHOD(to_str, (matrix_ptr<abstract>), string);
inline auto operator<<(std::ostream& os, matrix a) -> std::ostream& {
return os << to_str(a.rep());
}
BOOST_OPENMETHOD_OVERRIDE(to_str, (matrix_ptr<dense>), string) {
return "a dense matrix";
}
BOOST_OPENMETHOD_OVERRIDE(to_str, (matrix_ptr<diagonal>), string) {
return "a diagonal matrix";
}
// -----------------------------------------------------------------------------
// matrix * matrix
BOOST_OPENMETHOD(times, (matrix_ptr<abstract>, matrix_ptr<abstract>), matrix);
// catch-all matrix * matrix -> dense
BOOST_OPENMETHOD_OVERRIDE(
times, (matrix_ptr<abstract> /*a*/, matrix_ptr<abstract> /*b*/), matrix) {
return matrix::make<dense>();
}
// diagonal * diagonal -> diagonal
BOOST_OPENMETHOD_OVERRIDE(
times, (matrix_ptr<diagonal> /*a*/, matrix_ptr<diagonal> /*b*/), matrix) {
return matrix::make<diagonal>();
}
inline auto operator*(matrix a, matrix b) -> matrix {
return times(a.rep(), b.rep());
}
// -----------------------------------------------------------------------------
// scalar * matrix
BOOST_OPENMETHOD(times, (double, matrix_ptr<abstract>), matrix);
// catch-all matrix * scalar -> dense
BOOST_OPENMETHOD_OVERRIDE(
times, (double /*a*/, matrix_ptr<abstract> /*b*/), matrix) {
return matrix::make<dense>();
}
BOOST_OPENMETHOD_OVERRIDE(
times, (double /*a*/, matrix_ptr<diagonal> /*b*/), matrix) {
return matrix::make<diagonal>();
}
// -----------------------------------------------------------------------------
// matrix * scalar
// just swap
inline auto times(matrix_ptr<abstract> a, double b) -> matrix {
return times(b, a);
}
// -----------------------------------------------------------------------------
// main
#define check(expr) \
{ \
if (!(expr)) { \
cerr << #expr << " failed\n"; \
} \
}
auto main() -> int {
using std::cerr;
using std::cout;
boost::openmethod::initialize();
matrix a = matrix::make<dense>();
matrix b = matrix::make<diagonal>();
double s = 1;
cout << a << "\n";
cout << b << "\n";
return 0;
}

View File

@@ -65,6 +65,12 @@ Provides a `virtual_traits` specialization that makes it possible to use a
Provides a `virtual_traits` specialization that makes it possible to use a
`std::unique_ptr` in place of a raw pointer or reference in virtual parameters.
[#boost_intrusive_ptr]
### link:{{BASE_URL}}/include/boost/openmethod/interop/boost_intrusive_ptr.hpp[<boost/openmethod/interop/boost_intrusive_ptr.hpp>]
Provides a `virtual_traits` specialization that makes it possible to use a
`boost::intrusive_ptr` in place of a raw pointer or reference in virtual parameters.
*The headers below are for advanced use*.
## Pre-Core Headers

View File

@@ -2844,14 +2844,21 @@ struct VirtualTraits {
//! Casts a virtual argument.
//!
//! Casts a virtual argument to the type expected by the overrider.
//! `cast` is responsible for passing virtual arguments from method to
//! overrider. In general, this requires some form of adjustment, because a
//! virtual parameter in the overrider usually has a different type than the
//! corresponding parameter in the method. Typically, the adjustment
//! consists of a cast, performed via `static_cast`, `dynamic_cast`, or
//! other means, depending on the type of the argument and the rtti policy
//! of the method. `cast` may return the adjusted argument by reference or
//! as a temporary value.
//!
//! @tparam T The type of a virtual parameter of a method.
//! @tparam U The type of a virtual parameter of an overrider.
//! @param arg The argument passed to a method call.
//! @return A reference to the argument, cast to `U`.
//! @tparam T The type of the virtual parameter in the method.
//! @tparam U The type of the virtual parameter in the overrider.
//! @param arg The argument passed to the method call.
//! @return A value that can be passed as a U.
template<typename U>
static auto cast(T arg) -> U;
static auto cast(T arg) -> detail::unspecified;
//! Rebind to a another class (smart pointers only).
//!

View File

@@ -10,6 +10,7 @@
#include <cstdio>
#include <charconv>
#include <random>
#include <string_view>
namespace boost::openmethod {

View File

@@ -0,0 +1,150 @@
// Copyright (c) 2018-2025 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_BOOST_INTRUSIVE_PTR_HPP
#define BOOST_OPENMETHOD_INTEROP_BOOST_INTRUSIVE_PTR_HPP
#include <boost/openmethod/core.hpp>
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include <memory>
namespace boost::openmethod {
//! Specialize virtual_traits for boost::intrusive_ptr.
//!
//! @tparam Class A class type, possibly cv-qualified.
//! @tparam Registry A @ref registry.
template<typename Class, class Registry>
struct virtual_traits<boost::intrusive_ptr<Class>, Registry> {
//! Rebind to a different element type.
//!
//! @tparam Other The new element type.
template<class Other>
using rebind = boost::intrusive_ptr<Other>;
//! `Class`, stripped from cv-qualifiers.
using virtual_type = std::remove_cv_t<Class>;
//! Return a reference to a non-modifiable `Class` object.
//! @param arg A reference to a `boost::intrusive_ptr<Class>`.
//! @return A reference to the object pointed to.
static auto peek(const boost::intrusive_ptr<Class>& arg) -> const Class& {
return *arg;
}
//! Cast method argument to overrider argument.
//!
//! Cast a `boost::intrusive_ptr` to a `boost::intrusive_ptr` to a derived
//! class, using a static cast if possible, and a dynamic cast otherwise.
//!
//! @tparam OverriderType The type required by the overrider (a
//! `boost::intrusive_ptr`).
//! @param obj The method's argument..
//! @return A `boost::intrusive_ptr` _value_.
template<class OverriderType>
static auto cast(const boost::intrusive_ptr<Class>& obj) {
using element_type = typename OverriderType::element_type;
if constexpr (detail::requires_dynamic_cast<Class*, element_type*>) {
// make it work with custom RTTI
return OverriderType(
&Registry::rtti::template dynamic_cast_ref<element_type&>(
*obj));
} else {
return boost::static_pointer_cast<element_type>(obj);
}
}
};
//! Specialize virtual_traits for const boost::intrusive_ptr&.
//!
//! @tparam Class A class type, possibly cv-qualified.
//! @tparam Registry A @ref registry.
template<class Class, class Registry>
struct virtual_traits<const boost::intrusive_ptr<Class>&, Registry> {
public:
//! Rebind to a different element type.
//!
//! @tparam Other The new element type.
template<class Other>
using rebind = boost::intrusive_ptr<Other>;
//! `Class`, stripped from cv-qualifiers.
using virtual_type = std::remove_cv_t<Class>;
//! Return a reference to a non-modifiable `Class` object.
//! @param arg A reference to a `boost::intrusive_ptr<Class>`.
//! @return A reference to the object pointed to.
static auto peek(const boost::intrusive_ptr<Class>& arg) -> const Class& {
return *arg;
}
//! Cast method argument to overrider argument.
//!
//! Cast a `boost::intrusive_ptr` to a `boost::intrusive_ptr` to a derived
//! class, using a static cast if possible, and a dynamic cast otherwise.
//!
//! @tparam OverriderType The type required by the overrider (a `const
//! boost::intrusive_ptr&`).
//! @param obj The method's argument..
//! @return A `boost::intrusive_ptr` _value_.
template<class OverriderType>
static decltype(auto) cast(const boost::intrusive_ptr<Class>& obj) {
if constexpr (std::is_same_v<
OverriderType, const boost::intrusive_ptr<Class>&>) {
return obj;
} else {
using element_type =
typename std::remove_reference_t<OverriderType>::element_type;
if constexpr (detail::requires_dynamic_cast<
Class*, element_type*>) {
// make it work with custom RTTI
return std::remove_const_t<
std::remove_reference_t<OverriderType>>(
&Registry::rtti::template dynamic_cast_ref<element_type&>(
*obj));
} else {
return boost::static_pointer_cast<element_type>(obj);
}
}
}
};
//! Alias for a `virtual_ptr<boost::intrusive_ptr<T>>`.
template<class Class, class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY>
using boost_intrusive_virtual_ptr =
virtual_ptr<boost::intrusive_ptr<Class>, Registry>;
//! Create a new object and return a `boost_intrusive_virtual_ptr` to it.
//!
//! Create an object using `std::make_shared`, and return a @ref
//! boost_intrusive_virtual_ptr pointing to it. Since the exact class of the
//! object is known, the `virtual_ptr` is created using @ref final_virtual_ptr.
//!
//! `Class` is _not_ required to be a polymorphic class.
//!
//! @tparam Class The class of the object to create.
//! @tparam Registry A @ref registry.
//! @tparam T Types of the arguments to pass to the constructor of `Class`.
//! @param args Arguments to pass to the constructor of `Class`.
//! @return A `boost_intrusive_virtual_ptr<Class, Registry>` pointing to a newly
//! created object of type `Class`.
template<
class Class, class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY,
typename... T>
inline auto make_boost_intrusive_virtual(T&&... args) {
return final_virtual_ptr<Registry>(intrusive_ptr<Class>(
new std::remove_cv_t<Class>(std::forward<T>(args)...)));
}
namespace aliases {
using boost::openmethod::boost_intrusive_virtual_ptr;
using boost::openmethod::make_boost_intrusive_virtual;
} // namespace aliases
} // namespace boost::openmethod
#endif

View File

@@ -167,15 +167,20 @@ struct virtual_traits<const std::shared_ptr<Class>&, Registry> {
//! @return A `std::shared_ptr` to the same object, cast to
//! `Derived::element_type`.
template<class Other>
static auto cast(const std::shared_ptr<Class>& obj) {
using namespace boost::openmethod::detail;
if constexpr (requires_dynamic_cast<Class*, Other>) {
return std::dynamic_pointer_cast<
typename shared_ptr_cast_traits<Other>::virtual_type>(obj);
static decltype(auto) cast(const std::shared_ptr<Class>& obj) {
if constexpr (std::is_same_v<Other, const std::shared_ptr<Class>&>) {
// avoid unnecessary copy
return obj;
} else {
return std::static_pointer_cast<
typename shared_ptr_cast_traits<Other>::virtual_type>(obj);
using namespace boost::openmethod::detail;
if constexpr (requires_dynamic_cast<Class*, Other>) {
return std::dynamic_pointer_cast<
typename shared_ptr_cast_traits<Other>::virtual_type>(obj);
} else {
return std::static_pointer_cast<
typename shared_ptr_cast_traits<Other>::virtual_type>(obj);
}
}
}
};

View File

@@ -70,8 +70,8 @@ struct va_args<ReturnType> {
ForwarderParameters...>::type \
BOOST_OPENMETHOD_GUIDE(NAME)(ForwarderParameters && ... args); \
template<typename... ForwarderParameters> \
inline auto NAME(ForwarderParameters&&... args) \
->typename ::boost::openmethod::detail::enable_forwarder< \
inline auto NAME(ForwarderParameters&&... args) -> \
typename ::boost::openmethod::detail::enable_forwarder< \
void, BOOST_OPENMETHOD_TYPE(NAME, ARGS, __VA_ARGS__), \
::boost::openmethod::detail::va_args<__VA_ARGS__>::return_type, \
ForwarderParameters...>::type { \
@@ -102,15 +102,13 @@ struct va_args<ReturnType> {
static auto next(Args&&... args) -> decltype(auto); \
}; \
inline auto BOOST_OPENMETHOD_OVERRIDERS( \
NAME)<__VA_ARGS__ ARGS>::has_next() \
->bool { \
NAME)<__VA_ARGS__ ARGS>::has_next() -> bool { \
return boost_openmethod_detail_locate_method_aux< \
void ARGS>::type::has_next<fn>(); \
} \
template<typename... Args> \
inline auto BOOST_OPENMETHOD_OVERRIDERS(NAME)<__VA_ARGS__ ARGS>::next( \
Args&&... args) \
->decltype(auto) { \
Args&&... args) -> decltype(auto) { \
return boost_openmethod_detail_locate_method_aux< \
void ARGS>::type::next<fn>(std::forward<Args>(args)...); \
}
@@ -125,7 +123,7 @@ struct va_args<ReturnType> {
#define BOOST_OPENMETHOD_DEFINE_OVERRIDER(NAME, ARGS, ...) \
BOOST_OPENMETHOD_DETAIL_REGISTER_OVERRIDER(NAME, ARGS, __VA_ARGS__) \
auto BOOST_OPENMETHOD_OVERRIDER(NAME, ARGS, __VA_ARGS__)::fn ARGS \
->boost::mp11::mp_back<boost::mp11::mp_list<__VA_ARGS__>>
-> boost::mp11::mp_back<boost::mp11::mp_list<__VA_ARGS__>>
#define BOOST_OPENMETHOD_OVERRIDE(NAME, ARGS, ...) \
BOOST_OPENMETHOD_DECLARE_OVERRIDER(NAME, ARGS, __VA_ARGS__) \
@@ -135,7 +133,7 @@ struct va_args<ReturnType> {
BOOST_OPENMETHOD_DECLARE_OVERRIDER(NAME, ARGS, __VA_ARGS__) \
BOOST_OPENMETHOD_DETAIL_REGISTER_OVERRIDER(NAME, ARGS, __VA_ARGS__) \
inline auto BOOST_OPENMETHOD_OVERRIDER(NAME, ARGS, __VA_ARGS__)::fn ARGS \
->boost::mp11::mp_back<boost::mp11::mp_list<__VA_ARGS__>>
-> boost::mp11::mp_back<boost::mp11::mp_list<__VA_ARGS__>>
#define BOOST_OPENMETHOD_CLASSES(...) \
BOOST_OPENMETHOD_REGISTER(::boost::openmethod::use_classes<__VA_ARGS__>)

View File

@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <vector>
#include <cstdint>
#include <string_view>
#ifdef _MSC_VER
#pragma warning(push)

View File

@@ -0,0 +1,169 @@
// Copyright (c) 2018-2025 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)
#include <iostream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <any>
#include <boost/openmethod.hpp>
#include <boost/openmethod/interop/boost_intrusive_ptr.hpp>
#include <boost/openmethod/initialize.hpp>
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include <boost/smart_ptr/intrusive_ref_counter.hpp>
#define BOOST_TEST_MODULE openmethod
#include <boost/test/unit_test.hpp>
using namespace boost::openmethod;
#define MAKE_CLASSES() \
struct Animal : boost::intrusive_ref_counter<Animal> { \
explicit Animal(std::string str) { \
name = std::move(str); \
} \
\
Animal(const Animal&) = delete; \
Animal(Animal&&) = default; \
virtual ~Animal() = default; \
\
std::string name; \
}; \
\
struct Dog : Animal { \
using Animal::Animal; \
}; \
\
struct Cat : virtual Animal { \
using Animal::Animal; \
}; \
\
BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat);
namespace BOOST_OPENMETHOD_GENSYM {
// -----------------------------------------------------------------------------
// pass virtual args by shared_ptr by value
MAKE_CLASSES();
BOOST_OPENMETHOD(
name, (virtual_<boost::intrusive_ptr<const Animal>>), std::string);
BOOST_OPENMETHOD_OVERRIDE(
name, (boost::intrusive_ptr<const Cat> cat), std::string) {
return cat->name + " the cat";
}
BOOST_OPENMETHOD_OVERRIDE(
name, (boost::intrusive_ptr<const Dog> dog), std::string) {
return dog->name + " the dog";
}
BOOST_AUTO_TEST_CASE(intrusive_ptr_by_value) {
initialize();
auto spot = boost::intrusive_ptr<const Dog>(new Dog("Spot"));
BOOST_TEST(name(spot) == "Spot the dog");
auto felix = boost::intrusive_ptr<const Cat>(new Cat("Felix"));
BOOST_TEST(name(felix) == "Felix the cat");
}
} // namespace BOOST_OPENMETHOD_GENSYM
namespace BOOST_OPENMETHOD_GENSYM {
// -----------------------------------------------------------------------------
// pass virtual args by shared_ptr by const&
MAKE_CLASSES();
BOOST_OPENMETHOD(
name, (virtual_<const boost::intrusive_ptr<const Animal>&>), std::string);
BOOST_OPENMETHOD_OVERRIDE(
name, (const boost::intrusive_ptr<const Cat>& cat), std::string) {
return cat->name + " the cat";
}
BOOST_OPENMETHOD_OVERRIDE(
name, (const boost::intrusive_ptr<const Dog>& dog), std::string) {
return dog->name + " the dog";
}
BOOST_AUTO_TEST_CASE(intrusive_ptr_by_const_ref) {
initialize();
auto spot = boost::intrusive_ptr<const Dog>(new Dog("Spot"));
BOOST_TEST(name(spot) == "Spot the dog");
auto felix = boost::intrusive_ptr<const Cat>(new Cat("Felix"));
BOOST_TEST(name(felix) == "Felix the cat");
}
} // namespace BOOST_OPENMETHOD_GENSYM
namespace BOOST_OPENMETHOD_GENSYM {
// -----------------------------------------------------------------------------
// virtual_ptr<intrusive_ptr> by value
MAKE_CLASSES();
BOOST_OPENMETHOD(
name, (boost_intrusive_virtual_ptr<const Animal>), std::string);
BOOST_OPENMETHOD_OVERRIDE(
name, (boost_intrusive_virtual_ptr<const Cat> cat), std::string) {
return cat->name + " the cat";
}
BOOST_OPENMETHOD_OVERRIDE(
name, (boost_intrusive_virtual_ptr<const Dog> dog), std::string) {
return dog->name + " the dog";
}
BOOST_AUTO_TEST_CASE(intrusive_virtual_ptr_by_value) {
initialize();
auto spot = make_boost_intrusive_virtual<const Dog>("Spot");
BOOST_TEST(name(spot) == "Spot the dog");
auto felix = make_boost_intrusive_virtual<const Cat>("Felix");
BOOST_TEST(name(felix) == "Felix the cat");
}
} // namespace BOOST_OPENMETHOD_GENSYM
namespace BOOST_OPENMETHOD_GENSYM {
// -----------------------------------------------------------------------------
// virtual_ptr<intrusive_ptr> by const&
MAKE_CLASSES();
BOOST_OPENMETHOD(
name, (const boost_intrusive_virtual_ptr<const Animal>&), std::string);
BOOST_OPENMETHOD_OVERRIDE(
name, (const boost_intrusive_virtual_ptr<const Cat>& cat), std::string) {
return cat->name + " the cat";
}
BOOST_OPENMETHOD_OVERRIDE(
name, (const boost_intrusive_virtual_ptr<const Dog>& dog), std::string) {
return dog->name + " the dog";
}
BOOST_AUTO_TEST_CASE(intrusive_virtual_ptr_by_const_ref) {
initialize();
auto spot = make_boost_intrusive_virtual<const Dog>("Spot");
BOOST_TEST(name(spot) == "Spot the dog");
auto felix = make_boost_intrusive_virtual<const Cat>("Felix");
BOOST_TEST(name(felix) == "Felix the cat");
}
} // namespace BOOST_OPENMETHOD_GENSYM

View File

@@ -0,0 +1,443 @@
// Copyright (c) 2018-2025 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or q at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/openmethod/interop/std_shared_ptr.hpp>
#include <boost/openmethod/interop/boost_intrusive_ptr.hpp>
#define BOOST_TEST_MODULE openmethod
#include <boost/test/unit_test.hpp>
#include <boost/utility/identity_type.hpp>
#include "test_virtual_ptr_value_semantics.hpp"
#include <memory>
#ifdef BOOST_GCC
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#endif
#ifdef __clang__
#pragma clang diagnostic ignored "-Wunused-local-typedefs"
#endif
struct using_shared_ptr {
template<class Class>
using ptr = std::shared_ptr<Class>;
template<class Class>
using virtual_ptr = shared_virtual_ptr<Class>;
template<class Class, class... Args>
static decltype(auto) make(Args&&... args) {
return std::make_shared<Class>(std::forward<Args>(args)...);
}
template<class Class, class... Args>
static decltype(auto) make_virtual(Args&&... args) {
return make_shared_virtual<Class>(std::forward<Args>(args)...);
}
static bool cast_moves() {
initialize();
ptr<Animal> animal = make<Dog>();
(void)std::static_pointer_cast<Dog>(animal);
return animal.get() == nullptr;
}
};
struct using_boost_intrusive_ptr {
template<class Class>
using ptr = boost::intrusive_ptr<Class>;
template<class Class>
using virtual_ptr = boost_intrusive_virtual_ptr<Class>;
template<class Class, class... Args>
static auto make(Args&&... args) {
return boost::intrusive_ptr<Class>(
new Class(std::forward<Args>(args)...));
}
template<class Class, class... Args>
static auto make_virtual(Args&&... args) {
return make_boost_intrusive_virtual<Class>(std::forward<Args>(args)...);
}
static bool cast_moves() {
return false;
}
};
using smart_pointers =
boost::mp11::mp_list<using_shared_ptr, using_boost_intrusive_ptr>;
#define USING_DECLARATIONS \
using animal_ptr = typename smart::template ptr<Animal>; \
using const_animal_ptr = typename smart::template ptr<const Animal>; \
using animal_virtual_ptr = typename smart::template virtual_ptr<Animal>; \
using const_animal_virtual_ptr = \
typename smart::template virtual_ptr<const Animal>; \
using dog_ptr = typename smart::template ptr<Dog>; \
using const_dog_ptr = typename smart::template ptr<const Dog>; \
using dog_virtual_ptr = typename smart::template virtual_ptr<Dog>; \
using const_dog_virtual_ptr = \
typename smart::template virtual_ptr<const Dog>; \
using cat_ptr = typename smart::template ptr<Cat>; \
using const_cat_ptr = typename smart::template ptr<const Cat>; \
using cat_virtual_ptr = typename smart::template virtual_ptr<Cat>; \
using const_cat_virtual_ptr = \
typename smart::template virtual_ptr<const Cat>;
BOOST_AUTO_TEST_CASE_TEMPLATE(
smart_pointer_value_semantics, smart, smart_pointers) {
USING_DECLARATIONS;
static_assert(SameSmartPtr<animal_ptr, dog_ptr, default_registry>);
static_assert(
!SameSmartPtr<animal_ptr, std::unique_ptr<Dog>, default_registry>);
static_assert(
std::is_same_v<typename animal_virtual_ptr::element_type, Animal>);
static_assert(std::is_same_v<
decltype(std::declval<animal_virtual_ptr>().get()), Animal*>);
static_assert(IsSmartPtr<animal_ptr, default_registry>);
static_assert(IsSmartPtr<const_animal_ptr, default_registry>);
static_assert(
std::is_same_v<decltype(*std::declval<animal_virtual_ptr>()), Animal&>);
initialize();
// construction and assignment from a plain pointer or reference is not
// allowed
static_assert(!construct_assign_ok<dog_virtual_ptr, Dog>);
static_assert(!construct_assign_ok<dog_virtual_ptr, Dog&&>);
static_assert(!construct_assign_ok<dog_virtual_ptr, const Dog&>);
static_assert(!construct_assign_ok<dog_virtual_ptr, const Dog*>);
// -------------------------------------------------------------------------
// construction and assignment from plain references and pointers
{
dog_virtual_ptr p{nullptr};
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
auto snoopy = smart::template make<Dog>();
dog_virtual_ptr p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
p = *&p;
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
auto hector = smart::template make<Dog>();
p = hector;
BOOST_TEST(p.get() == hector.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
}
{
auto snoopy = smart::template make<Dog>();
animal_virtual_ptr p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
auto felix = smart::template make<Cat>();
p = felix;
BOOST_TEST(p.get() == felix.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Cat>);
}
{
auto snoopy = smart::template make<const Dog>();
const_dog_virtual_ptr p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
auto hector = smart::template make<const Dog>();
p = hector;
BOOST_TEST(p.get() == hector.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
}
{
const auto snoopy = smart::template make<const Dog>();
const_animal_virtual_ptr p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
auto felix = smart::template make<const Cat>();
p = felix;
BOOST_TEST(p.get() == felix.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Cat>);
}
{
auto snoopy = smart::template make<Dog>();
dog_virtual_ptr p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
auto hector = smart::template make<Dog>();
p = hector;
BOOST_TEST(p.get() == hector.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
}
{
auto snoopy = smart::template make<Dog>();
animal_virtual_ptr p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
auto felix = smart::template make<Cat>();
p = felix;
BOOST_TEST(p.get() == felix.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Cat>);
}
{
auto snoopy = smart::template make<const Dog>();
const_dog_virtual_ptr p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
auto hector = smart::template make<const Dog>();
p = hector;
BOOST_TEST(p.get() == hector.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
}
{
auto snoopy = smart::template make<const Dog>();
const_animal_virtual_ptr p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
auto felix = smart::template make<const Cat>();
p = felix;
BOOST_TEST(p.get() == felix.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Cat>);
}
// dog_virtual_ptr p{Dog()};
static_assert(!construct_assign_ok<dog_virtual_ptr, Dog&&>);
// -------------------------------------------------------------------------
// construction and assignment from other shared_virtual_ptr
{
// dog_virtual_ptr(const dog_virtual_ptr&)
auto snoopy = smart::template make<Dog>();
const dog_virtual_ptr p(snoopy);
dog_virtual_ptr q(p);
BOOST_TEST(q.get() == snoopy.get());
BOOST_TEST(q.vptr() == default_registry::template static_vptr<Dog>);
}
{
// dog_virtual_ptr(dog_virtual_ptr&)
auto snoopy = smart::template make<Dog>();
dog_virtual_ptr p(snoopy);
dog_virtual_ptr q(p);
BOOST_TEST(q.get() == snoopy.get());
BOOST_TEST(q.vptr() == default_registry::template static_vptr<Dog>);
}
{
// dog_virtual_ptr(dog_virtual_ptr&&)
auto snoopy = smart::template make<Dog>();
dog_virtual_ptr p(snoopy);
dog_virtual_ptr q(std::move(p));
BOOST_TEST(q.get() == snoopy.get());
BOOST_TEST(q.vptr() == default_registry::template static_vptr<Dog>);
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
// animal_virtual_ptr(const dog_virtual_ptr&)
auto snoopy = smart::template make<Dog>();
const dog_virtual_ptr p(snoopy);
animal_virtual_ptr base(p);
BOOST_TEST(base.get() == snoopy.get());
BOOST_TEST(base.vptr() == default_registry::template static_vptr<Dog>);
}
{
// shared_virtual_ptr<const Dog>(const dog_virtual_ptr&)
auto snoopy = smart::template make<Dog>();
const dog_virtual_ptr p(snoopy);
const_dog_virtual_ptr const_q(p);
BOOST_TEST(const_q.get() == snoopy.get());
BOOST_TEST(
const_q.vptr() == default_registry::template static_vptr<Dog>);
}
{
// shared_virtual_ptr<const Animal>(const dog_virtual_ptr&)
auto snoopy = smart::template make<Dog>();
const dog_virtual_ptr p(snoopy);
const_animal_virtual_ptr const_base_q(p);
BOOST_TEST(const_base_q.get() == snoopy.get());
BOOST_TEST(
const_base_q.vptr() == default_registry::template static_vptr<Dog>);
}
{
// dog_virtual_ptr()
dog_virtual_ptr p{nullptr};
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
dog_virtual_ptr p{dog_ptr()};
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
// -------------------------------------------------------------------------
// assignment
{
dog_virtual_ptr p;
auto snoopy = smart::template make<Dog>();
p = snoopy;
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
}
{
dog_virtual_ptr p;
auto snoopy = smart::template make<Dog>();
p = snoopy;
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == default_registry::template static_vptr<Dog>);
}
{
auto p = smart::template make_virtual<Dog>();
p = nullptr;
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
auto p = smart::template make_virtual<Dog>();
p = dog_ptr();
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
static_assert(!construct_assign_ok<dog_virtual_ptr, const Dog&>);
static_assert(!construct_assign_ok<dog_virtual_ptr, const Dog*>);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(cast_smart_pointer_value, smart, smart_pointers) {
USING_DECLARATIONS;
initialize();
animal_ptr animal = smart::template make<Dog>();
dog_ptr dog =
virtual_traits<animal_ptr, default_registry>::template cast<dog_ptr>(
animal);
BOOST_TEST(dog.get() == animal.get());
}
BOOST_AUTO_TEST_CASE_TEMPLATE(
cast_smart_ptr_lvalue_reference, smart, smart_pointers) {
USING_DECLARATIONS;
initialize();
animal_ptr animal = smart::template make<Dog>();
dog_ptr dog =
virtual_traits<const animal_ptr&, default_registry>::template cast<
const dog_ptr&>(animal);
BOOST_TEST(dog.get() == animal.get());
dog_ptr dog2 = virtual_traits<
const dog_ptr&, default_registry>::template cast<const dog_ptr&>(dog);
BOOST_TEST(dog2.get() == dog.get());
}
BOOST_AUTO_TEST_CASE_TEMPLATE(
cast_smart_ptr_xvalue_reference, smart, smart_pointers) {
USING_DECLARATIONS;
initialize();
animal_ptr animal = smart::template make<Dog>();
auto p = animal.get();
auto dog =
virtual_traits<animal_ptr, default_registry>::template cast<dog_ptr>(
std::move(animal));
BOOST_TEST(dog.get() == p);
if (smart::cast_moves()) {
BOOST_TEST(animal.get() == nullptr);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(
cast_shared_virtual_ptr_value, smart, smart_pointers) {
USING_DECLARATIONS;
initialize();
animal_virtual_ptr base = smart::template make<Dog>();
auto derived = virtual_traits<
animal_virtual_ptr, default_registry>::template cast<dog_ptr>(base);
BOOST_TEST(derived.get() == base.get());
BOOST_TEST(base.vptr() == default_registry::static_vptr<Dog>);
BOOST_TEST(derived.vptr() == default_registry::static_vptr<Dog>);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(
cast_shared_virtual_ptr_lvalue_reference, smart, smart_pointers) {
USING_DECLARATIONS;
initialize();
animal_virtual_ptr base = smart::template make<Dog>();
auto derived = virtual_traits<const animal_virtual_ptr&, default_registry>::
template cast<const dog_virtual_ptr&>(base);
BOOST_TEST(derived.get() == base.get());
BOOST_TEST(base.vptr() == default_registry::static_vptr<Dog>);
BOOST_TEST(derived.vptr() == default_registry::static_vptr<Dog>);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(
cast_shared_virtual_ptr_xvalue_reference, smart, smart_pointers) {
USING_DECLARATIONS;
initialize();
animal_virtual_ptr base = smart::template make<Dog>();
auto p = base.get();
auto derived =
virtual_traits<animal_virtual_ptr, default_registry>::template cast<
dog_virtual_ptr>(std::move(base));
BOOST_TEST(derived.get() == p);
BOOST_TEST(derived.vptr() == default_registry::static_vptr<Dog>);
if (smart::cast_moves()) {
BOOST_TEST(base.get() == nullptr);
}
}
template struct check_illegal_smart_ops<
std::shared_ptr, std::unique_ptr, direct_vector>;
template struct check_illegal_smart_ops<
boost::intrusive_ptr, std::unique_ptr, direct_vector>;

View File

@@ -116,6 +116,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(
unique_virtual_ptr<Dog, Registry> p(std::make_unique<Dog>());
auto dog = p.get();
unique_virtual_ptr<Dog, Registry> q(std::move(p));
// coverity[use_after_move]
BOOST_TEST(q.get() == dog);
BOOST_TEST(q.vptr() == Registry::template static_vptr<Dog>);
BOOST_TEST(p.get() == nullptr);
@@ -127,6 +128,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(
unique_virtual_ptr<Dog, Registry> p(std::make_unique<Dog>());
auto dog = p.get();
unique_virtual_ptr<Animal, Registry> q(std::move(p));
// coverity[use_after_move]
BOOST_TEST(q.get() == dog);
BOOST_TEST(q.vptr() == Registry::template static_vptr<Dog>);
BOOST_TEST(p.get() == nullptr);

View File

@@ -168,6 +168,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(
Dog snoopy;
virtual_ptr<Dog, Registry> p(snoopy);
virtual_ptr<Dog, Registry> q(std::move(p));
// coverity[use_after_move]
BOOST_TEST(q.get() == &snoopy);
BOOST_TEST(q.vptr() == Registry::template static_vptr<Dog>);
BOOST_TEST(p.get() == &snoopy);

View File

@@ -6,10 +6,16 @@
#ifndef TEST_VIRTUAL_PTR_VALUE_SEMANTICS_HPP
#define TEST_VIRTUAL_PTR_VALUE_SEMANTICS_HPP
#include <type_traits>
#include <boost/mp11.hpp>
#include <boost/openmethod.hpp>
#include <boost/openmethod/policies/vptr_map.hpp>
#include <boost/openmethod/initialize.hpp>
#include <boost/openmethod/policies/vptr_map.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>
#include <boost/openmethod/interop/boost_intrusive_ptr.hpp>
#include <boost/openmethod/interop/std_unique_ptr.hpp>
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include <boost/smart_ptr/intrusive_ref_counter.hpp>
#include "test_util.hpp"
@@ -19,7 +25,7 @@ using namespace boost::openmethod;
using namespace policies;
using namespace detail;
struct Animal {
struct Animal : boost::intrusive_ref_counter<Animal> {
virtual ~Animal() {
}
@@ -31,6 +37,12 @@ struct Cat : virtual Animal {};
struct Dog : Animal {};
BOOST_OPENMETHOD_CLASSES(Animal, Cat, Dog);
struct id;
// without following line, no methods, no v-tables
auto instantiate_a_method = &method<id, auto(virtual_ptr<Animal>)->void>::fn;
template<class Left, class Right>
constexpr bool construct_assign_ok =
std::is_constructible_v<Left, Right> && std::is_assignable_v<Left, Right>;