mirror of
https://github.com/boostorg/openmethod.git
synced 2026-01-20 16:52:12 +00:00
Compare commits
15 Commits
develop
...
copilot/ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22673b0f45 | ||
|
|
508687c5d7 | ||
|
|
2a3145718b | ||
|
|
8e1603766e | ||
|
|
39b8412b67 | ||
|
|
509fcaaf4b | ||
|
|
d2885bad94 | ||
|
|
7e44f683ea | ||
|
|
b486642748 | ||
|
|
fe45caf13c | ||
|
|
88984a1d14 | ||
|
|
dc906d8a1f | ||
|
|
63d23e79a4 | ||
|
|
530de74f27 | ||
|
|
b420157916 |
@@ -92,30 +92,21 @@ 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 (BOOST_OPENMETHOD_BUILD_TESTS)
|
||||
if (BUILD_TESTING OR 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)
|
||||
|
||||
1
_codeql_detected_source_root
Symbolic link
1
_codeql_detected_source_root
Symbolic link
@@ -0,0 +1 @@
|
||||
.
|
||||
@@ -3,153 +3,146 @@
|
||||
// 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 namespace boost::openmethod::aliases;
|
||||
using boost::openmethod::make_shared_virtual;
|
||||
using boost::openmethod::shared_virtual_ptr;
|
||||
using boost::openmethod::virtual_ptr;
|
||||
|
||||
struct abstract {
|
||||
int ref_count = 0;
|
||||
struct matrix {
|
||||
virtual ~matrix() {
|
||||
}
|
||||
virtual auto at(int row, int col) const -> double = 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 dense_matrix : matrix {
|
||||
virtual auto at(int /*row*/, int /*col*/) const -> double {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct dense : abstract {
|
||||
static constexpr const char* type = "dense";
|
||||
struct diagonal_matrix : matrix {
|
||||
virtual auto at(int /*row*/, int /*col*/) const -> double {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(destroy, (matrix_ptr<dense> rep), void) {
|
||||
delete rep.get();
|
||||
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...";
|
||||
}
|
||||
|
||||
struct diagonal : abstract {
|
||||
static constexpr const char* type = "diagonal";
|
||||
};
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(destroy, (matrix_ptr<diagonal> rep), void) {
|
||||
delete rep.get();
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
to_json, (virtual_ptr<const diagonal_matrix>), string) {
|
||||
return "json for diagonal matrix...";
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(abstract, dense, diagonal, registry);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// matrix * matrix
|
||||
|
||||
BOOST_OPENMETHOD(
|
||||
times, (matrix_ptr<abstract>, matrix_ptr<abstract>), matrix, registry);
|
||||
times, (shared_virtual_ptr<const matrix>, shared_virtual_ptr<const matrix>),
|
||||
shared_virtual_ptr<const matrix>);
|
||||
|
||||
// catch-all matrix * matrix -> dense
|
||||
// catch-all matrix * matrix -> dense_matrix
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
times, (matrix_ptr<abstract> /*a*/, matrix_ptr<abstract> /*b*/), matrix) {
|
||||
return matrix::make<dense>();
|
||||
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>();
|
||||
}
|
||||
|
||||
// diagonal * diagonal -> diagonal
|
||||
// diagonal_matrix * diagonal_matrix -> diagonal_matrix
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
times, (matrix_ptr<diagonal> /*a*/, matrix_ptr<diagonal> /*b*/), matrix) {
|
||||
return matrix::make<diagonal>();
|
||||
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>();
|
||||
}
|
||||
|
||||
inline auto operator*(matrix a, matrix b) -> matrix {
|
||||
return times(a.rep(), b.rep());
|
||||
inline auto operator*(shared_ptr<const matrix> a, shared_ptr<const matrix> b)
|
||||
-> shared_virtual_ptr<const matrix> {
|
||||
return times(a, b);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// scalar * matrix
|
||||
|
||||
BOOST_OPENMETHOD(times, (double, matrix_ptr<abstract>), matrix, registry);
|
||||
BOOST_OPENMETHOD(
|
||||
times, (double, shared_virtual_ptr<const matrix>),
|
||||
shared_virtual_ptr<const matrix>);
|
||||
|
||||
// catch-all matrix * scalar -> dense
|
||||
// catch-all matrix * scalar -> dense_matrix
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
times, (double /*a*/, matrix_ptr<abstract> /*b*/), matrix) {
|
||||
return matrix::make<dense>();
|
||||
times, (double /*a*/, shared_virtual_ptr<const matrix> /*b*/),
|
||||
shared_virtual_ptr<const dense_matrix>) {
|
||||
return make_shared_virtual<dense_matrix>();
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
times, (double /*a*/, matrix_ptr<diagonal> /*b*/), matrix) {
|
||||
return matrix::make<diagonal>();
|
||||
times, (double /*a*/, shared_virtual_ptr<const diagonal_matrix> /*b*/),
|
||||
shared_virtual_ptr<const diagonal_matrix>) {
|
||||
return make_shared_virtual<diagonal_matrix>();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// matrix * scalar
|
||||
|
||||
// just swap
|
||||
inline auto times(matrix_ptr<abstract> a, double b) -> matrix {
|
||||
inline auto times(shared_virtual_ptr<const matrix> a, double b)
|
||||
-> shared_virtual_ptr<const matrix> {
|
||||
return times(b, a);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// main
|
||||
|
||||
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";
|
||||
}
|
||||
#define check(expr) \
|
||||
{ \
|
||||
if (!(expr)) { \
|
||||
cerr << #expr << " failed\n"; \
|
||||
} \
|
||||
}
|
||||
|
||||
auto main() -> int {
|
||||
using std::cerr;
|
||||
using std::cout;
|
||||
|
||||
boost::openmethod::initialize<registry>();
|
||||
boost::openmethod::initialize();
|
||||
|
||||
matrix a = matrix::make<dense>();
|
||||
matrix b = matrix::make<diagonal>();
|
||||
shared_ptr<const matrix> a = make_shared<dense_matrix>();
|
||||
shared_ptr<const matrix> b = make_shared<diagonal_matrix>();
|
||||
double s = 1;
|
||||
|
||||
cout << a << "\n";
|
||||
cout << b << "\n";
|
||||
#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
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -14,5 +14,5 @@ BOOST_OPENMETHOD_OVERRIDE(
|
||||
return 5000.0;
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Employee);
|
||||
BOOST_OPENMETHOD_CLASSES(Employee)
|
||||
// end::content[]
|
||||
|
||||
@@ -14,6 +14,6 @@ BOOST_OPENMETHOD_OVERRIDE(
|
||||
return next(emp) + emp->sales * 0.05; // base + commission
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman);
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)
|
||||
|
||||
// end::content[]
|
||||
|
||||
@@ -12,5 +12,5 @@ BOOST_OPENMETHOD_DEFINE_OVERRIDER(
|
||||
return 5000.0;
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Employee);
|
||||
BOOST_OPENMETHOD_CLASSES(Employee)
|
||||
// end::content[]
|
||||
|
||||
@@ -16,4 +16,4 @@ BOOST_OPENMETHOD_OVERRIDE(
|
||||
}
|
||||
// end::content[]
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman);
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
#include "roles.hpp"
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Employee);
|
||||
BOOST_OPENMETHOD_CLASSES(Employee)
|
||||
// end::content[]
|
||||
|
||||
@@ -16,4 +16,4 @@ BOOST_OPENMETHOD_OVERRIDE(
|
||||
}
|
||||
// end::content[]
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman);
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
#include "roles.hpp"
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(employees::Employee);
|
||||
BOOST_OPENMETHOD_CLASSES(employees::Employee)
|
||||
// end::content[]
|
||||
|
||||
@@ -17,7 +17,7 @@ BOOST_OPENMETHOD_OVERRIDE(
|
||||
emp->sales * 0.05; // base + commission
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(employees::Employee, Salesman);
|
||||
BOOST_OPENMETHOD_CLASSES(employees::Employee, Salesman)
|
||||
|
||||
} // namespace sales
|
||||
// end::content[]
|
||||
|
||||
@@ -71,7 +71,7 @@ BOOST_OPENMETHOD_OVERRIDE(
|
||||
}
|
||||
|
||||
// ...and let's not forget to register the classes
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman);
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)
|
||||
// end::overriders[]
|
||||
|
||||
// tag::main[]
|
||||
|
||||
@@ -66,7 +66,7 @@ BOOST_OPENMETHOD_OVERRIDE(
|
||||
}
|
||||
|
||||
// ...and let's not forget to register the classes
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman);
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)
|
||||
// end::overriders[]
|
||||
|
||||
// tag::main[]
|
||||
|
||||
@@ -12,10 +12,7 @@ add_compile_definitions(BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS)
|
||||
|
||||
add_library(boost_openmethod-shared SHARED extensions.cpp)
|
||||
target_link_libraries(boost_openmethod-shared Boost::openmethod)
|
||||
set_target_properties(boost_openmethod-shared PROPERTIES
|
||||
ENABLE_EXPORTS ON
|
||||
OUTPUT_NAME shared
|
||||
)
|
||||
set_target_properties(boost_openmethod-shared PROPERTIES ENABLE_EXPORTS ON)
|
||||
|
||||
add_executable(boost_openmethod-static static_main.cpp)
|
||||
target_link_libraries(boost_openmethod-static Boost::openmethod Boost::dll boost_openmethod-shared)
|
||||
@@ -39,10 +36,7 @@ add_library(boost_openmethod-indirect_shared SHARED indirect_extensions.cpp)
|
||||
target_compile_definitions(
|
||||
boost_openmethod-indirect_shared PUBLIC BOOST_OPENMETHOD_DEFAULT_REGISTRY=indirect_registry)
|
||||
target_link_libraries(boost_openmethod-indirect_shared PRIVATE Boost::openmethod Boost::dll)
|
||||
set_target_properties(boost_openmethod-indirect_shared PROPERTIES
|
||||
ENABLE_EXPORTS ON
|
||||
OUTPUT_NAME indirect_shared
|
||||
)
|
||||
set_target_properties(boost_openmethod-indirect_shared PROPERTIES ENABLE_EXPORTS ON)
|
||||
|
||||
add_executable(boost_openmethod-indirect indirect_main.cpp)
|
||||
target_compile_definitions(
|
||||
|
||||
@@ -6,7 +6,7 @@ An _open-method_ is a free-standing function that has one or more _virtual_
|
||||
_parameters_. When it is called, it forwards to an _overrider_ selected from a
|
||||
set by examining the dynamic types of the virtual parameters.
|
||||
|
||||
If this sounds like a virtual function, that's because an open-method
|
||||
If this sounds like a virtual function, that's because because an open-method
|
||||
with one virtual parameter is equivalent to a virtual function - with one big
|
||||
difference: it exists outside of classes.
|
||||
|
||||
|
||||
@@ -65,12 +65,6 @@ 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
|
||||
|
||||
@@ -2844,21 +2844,14 @@ struct VirtualTraits {
|
||||
|
||||
//! Casts a virtual argument.
|
||||
//!
|
||||
//! `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.
|
||||
//! Casts a virtual argument to the type expected by the overrider.
|
||||
//!
|
||||
//! @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.
|
||||
//! @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`.
|
||||
template<typename U>
|
||||
static auto cast(T arg) -> detail::unspecified;
|
||||
static auto cast(T arg) -> U;
|
||||
|
||||
//! Rebind to a another class (smart pointers only).
|
||||
//!
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <cstdio>
|
||||
#include <charconv>
|
||||
#include <random>
|
||||
#include <string_view>
|
||||
|
||||
namespace boost::openmethod {
|
||||
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
// 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
|
||||
@@ -167,20 +167,15 @@ 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 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 {
|
||||
using namespace boost::openmethod::detail;
|
||||
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);
|
||||
} else {
|
||||
return std::static_pointer_cast<
|
||||
typename shared_ptr_cast_traits<Other>::virtual_type>(obj);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
413
include/boost/openmethod/policies/minimal_perfect_hash.hpp
Normal file
413
include/boost/openmethod/policies/minimal_perfect_hash.hpp
Normal file
@@ -0,0 +1,413 @@
|
||||
// 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_POLICY_MINIMAL_PERFECT_HASH_HPP
|
||||
#define BOOST_OPENMETHOD_POLICY_MINIMAL_PERFECT_HASH_HPP
|
||||
|
||||
#include <boost/openmethod/preamble.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4702) // unreachable code
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(UINTPTR_MAX)
|
||||
using uintptr = std::uintptr_t;
|
||||
constexpr uintptr uintptr_max = UINTPTR_MAX;
|
||||
#else
|
||||
static_assert(
|
||||
sizeof(std::size_t) == sizeof(void*),
|
||||
"This implementation requires that size_t and void* have the same size.");
|
||||
using uintptr = std::size_t;
|
||||
constexpr uintptr uintptr_max = (std::numeric_limits<std::size_t>::max)();
|
||||
#endif
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace boost::openmethod {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Registry>
|
||||
std::vector<type_id> minimal_perfect_hash_control;
|
||||
|
||||
template<class Registry>
|
||||
std::vector<std::size_t> minimal_perfect_hash_displacements;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace policies {
|
||||
|
||||
//! Hash type ids using a minimal perfect hash function.
|
||||
//!
|
||||
//! `minimal_perfect_hash` implements the @ref type_hash policy using a hash
|
||||
//! function in the form `H(x)=(M*x)>>N`. It uses the PtHash algorithm to
|
||||
//! determine values for `M` and `N` that result in a minimal perfect hash
|
||||
//! function for the set of registered type_ids. This means that the hash
|
||||
//! function is collision-free and the codomain is approximately the size of
|
||||
//! the domain, resulting in a dense range [0, 1.1*n-1] for n inputs.
|
||||
//!
|
||||
//! Unlike @ref fast_perfect_hash, which uses a hash table of size 2^k
|
||||
//! (typically larger than needed) and may have unused slots, this policy
|
||||
//! uses approximately 1.1*n slots for n type_ids (allowing up to 10% waste).
|
||||
//! This minimizes memory usage while maintaining good search performance
|
||||
//! during initialization.
|
||||
struct minimal_perfect_hash : type_hash {
|
||||
|
||||
//! Cannot find hash factors
|
||||
struct search_error : openmethod_error {
|
||||
//! Number of attempts to find hash factors
|
||||
std::size_t attempts;
|
||||
//! Number of buckets used in the last attempt
|
||||
std::size_t buckets;
|
||||
|
||||
//! Write a short description to an output stream
|
||||
//! @param os The output stream
|
||||
//! @tparam Registry The registry
|
||||
//! @tparam Stream A @ref LightweightOutputStream
|
||||
template<class Registry, class Stream>
|
||||
auto write(Stream& os) const -> void;
|
||||
};
|
||||
|
||||
using errors = std::variant<search_error>;
|
||||
|
||||
//! A TypeHashFn metafunction.
|
||||
//!
|
||||
//! @tparam Registry The registry containing this policy
|
||||
template<class Registry>
|
||||
class fn {
|
||||
static std::size_t mult;
|
||||
static std::size_t shift;
|
||||
static std::size_t table_size; // N for minimal perfect hash
|
||||
static std::size_t num_groups;
|
||||
static std::uint32_t group_mult; // Smaller type to avoid overflow
|
||||
static std::size_t group_shift;
|
||||
|
||||
static void check(std::size_t index, type_id type);
|
||||
|
||||
template<class InitializeContext, class... Options>
|
||||
static void initialize(
|
||||
const InitializeContext& ctx, std::vector<type_id>& buckets,
|
||||
const std::tuple<Options...>& options);
|
||||
|
||||
public:
|
||||
//! Find the hash factors using PtHash algorithm
|
||||
//!
|
||||
//! Uses the PtHash algorithm to find:
|
||||
//! - Pilot hash parameters (M, N) for H(x) = (M * x) >> N
|
||||
//! - Bucket assignment parameters
|
||||
//! - Displacement values for each bucket to achieve minimal perfect hashing
|
||||
//!
|
||||
//! If no suitable values are found, calls the error handler with
|
||||
//! a @ref search_error object then calls `abort`.
|
||||
//!
|
||||
//! @tparam Context An @ref InitializeContext.
|
||||
//! @param ctx A Context object.
|
||||
//! @return A pair containing the minimum (0) and maximum (n-1) hash values.
|
||||
template<class Context, class... Options>
|
||||
static auto
|
||||
initialize(const Context& ctx, const std::tuple<Options...>& options) {
|
||||
if constexpr (Registry::has_runtime_checks) {
|
||||
initialize(
|
||||
ctx, detail::minimal_perfect_hash_control<Registry>, options);
|
||||
} else {
|
||||
std::vector<type_id> buckets;
|
||||
initialize(ctx, buckets, options);
|
||||
}
|
||||
|
||||
return std::pair{std::size_t(0), table_size - 1};
|
||||
}
|
||||
|
||||
//! Hash a type id using the PtHash algorithm
|
||||
//!
|
||||
//! Hash a type id using H(x) = (pilot(x) + disp[group(x)]) % N
|
||||
//! where pilot(x) = (M * x) >> S and group(x) = (GM * x) >> GS.
|
||||
//!
|
||||
//! If `Registry` contains the @ref runtime_checks policy, checks that
|
||||
//! the type id is valid, i.e. if it was present in the set passed to
|
||||
//! @ref initialize. Its absence indicates that a class involved in a
|
||||
//! method definition, method overrider, or method call was not
|
||||
//! registered. In this case, signal a @ref missing_class using
|
||||
//! the registry's @ref error_handler if present; then calls `abort`.
|
||||
//!
|
||||
//! @param type The type_id to hash
|
||||
//! @return The hash value
|
||||
BOOST_FORCEINLINE
|
||||
static auto hash(type_id type) -> std::size_t {
|
||||
auto pilot = (mult * reinterpret_cast<uintptr>(type)) >> shift;
|
||||
auto group = (group_mult * reinterpret_cast<uintptr>(type)) >> group_shift;
|
||||
auto index = (pilot + detail::minimal_perfect_hash_displacements<Registry>[group]) % table_size;
|
||||
|
||||
if constexpr (Registry::has_runtime_checks) {
|
||||
check(index, type);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
//! Releases the memory allocated by `initialize`.
|
||||
//!
|
||||
//! @tparam Options... Zero or more option types, deduced from the function
|
||||
//! arguments.
|
||||
//! @param options Zero or more option objects.
|
||||
template<class... Options>
|
||||
static auto finalize(const std::tuple<Options...>&) -> void {
|
||||
detail::minimal_perfect_hash_control<Registry>.clear();
|
||||
detail::minimal_perfect_hash_displacements<Registry>.clear();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template<class Registry>
|
||||
std::size_t minimal_perfect_hash::fn<Registry>::mult;
|
||||
|
||||
template<class Registry>
|
||||
std::size_t minimal_perfect_hash::fn<Registry>::shift;
|
||||
|
||||
template<class Registry>
|
||||
std::size_t minimal_perfect_hash::fn<Registry>::table_size;
|
||||
|
||||
template<class Registry>
|
||||
std::size_t minimal_perfect_hash::fn<Registry>::num_groups;
|
||||
|
||||
template<class Registry>
|
||||
std::uint32_t minimal_perfect_hash::fn<Registry>::group_mult;
|
||||
|
||||
template<class Registry>
|
||||
std::size_t minimal_perfect_hash::fn<Registry>::group_shift;
|
||||
|
||||
template<class Registry>
|
||||
template<class InitializeContext, class... Options>
|
||||
void minimal_perfect_hash::fn<Registry>::initialize(
|
||||
const InitializeContext& ctx, std::vector<type_id>& buckets,
|
||||
const std::tuple<Options...>& options) {
|
||||
(void)options;
|
||||
|
||||
const auto N = std::distance(ctx.classes_begin(), ctx.classes_end());
|
||||
|
||||
if constexpr (InitializeContext::template has_option<trace>) {
|
||||
ctx.tr << "Finding minimal perfect hash using PtHash for " << N << " types\n";
|
||||
}
|
||||
|
||||
// Table size is N * 1.1 to allow up to 10% waste (makes finding hash easier)
|
||||
// Formula: ceil(N * 1.1) = (N * 11 + 9) / 10 ensures proper rounding for all N
|
||||
constexpr std::size_t WASTE_FACTOR_NUMERATOR = 11; // 1.1 = 11/10
|
||||
constexpr std::size_t WASTE_FACTOR_DENOMINATOR = 10;
|
||||
constexpr std::size_t ROUNDING_ADJUSTMENT = 9; // For ceiling division
|
||||
table_size = (N * WASTE_FACTOR_NUMERATOR + ROUNDING_ADJUSTMENT) / WASTE_FACTOR_DENOMINATOR;
|
||||
|
||||
if (table_size == 0) {
|
||||
shift = 0;
|
||||
mult = 1;
|
||||
num_groups = 0;
|
||||
group_mult = 1;
|
||||
group_shift = 0;
|
||||
detail::minimal_perfect_hash_displacements<Registry>.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (table_size == 1) {
|
||||
// Special case: only one type
|
||||
constexpr std::size_t bits_per_type_id = 8 * sizeof(type_id);
|
||||
shift = bits_per_type_id;
|
||||
mult = 1;
|
||||
num_groups = 1;
|
||||
group_mult = 1;
|
||||
group_shift = bits_per_type_id;
|
||||
detail::minimal_perfect_hash_displacements<Registry>.assign(1, 0);
|
||||
buckets.resize(1);
|
||||
for (auto iter = ctx.classes_begin(); iter != ctx.classes_end(); ++iter) {
|
||||
for (auto type_iter = iter->type_id_begin();
|
||||
type_iter != iter->type_id_end(); ++type_iter) {
|
||||
buckets[0] = *type_iter;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Collect all type_ids
|
||||
std::vector<type_id> keys;
|
||||
for (auto iter = ctx.classes_begin(); iter != ctx.classes_end(); ++iter) {
|
||||
for (auto type_iter = iter->type_id_begin();
|
||||
type_iter != iter->type_id_end(); ++type_iter) {
|
||||
keys.push_back(*type_iter);
|
||||
}
|
||||
}
|
||||
|
||||
// Constants for PtHash algorithm
|
||||
constexpr std::size_t DEFAULT_RANDOM_SEED = 13081963; // Same seed as fast_perfect_hash
|
||||
constexpr std::size_t MAX_PASSES = 10;
|
||||
constexpr std::size_t MAX_ATTEMPTS = 100000;
|
||||
constexpr std::size_t DEFAULT_GROUP_DIVISOR = 4; // N/4 groups for balance between memory and speed
|
||||
constexpr std::size_t DISTRIBUTION_FACTOR = 2; // 2*N range for better distribution
|
||||
constexpr std::size_t bits_per_type_id = 8 * sizeof(type_id);
|
||||
|
||||
std::default_random_engine rnd(DEFAULT_RANDOM_SEED);
|
||||
std::uniform_int_distribution<std::size_t> uniform_dist;
|
||||
std::size_t total_attempts = 0;
|
||||
|
||||
// PtHash algorithm: partition keys into groups, then find displacements
|
||||
// Number of groups: typically sqrt(N) to N/4 for good performance
|
||||
num_groups = (std::max)(std::size_t(1), table_size / DEFAULT_GROUP_DIVISOR);
|
||||
if (num_groups > table_size) num_groups = table_size;
|
||||
|
||||
// Calculate bits needed for num_groups
|
||||
std::size_t GM = 0;
|
||||
std::size_t power = 1;
|
||||
while (power < num_groups) {
|
||||
power <<= 1;
|
||||
++GM;
|
||||
}
|
||||
group_shift = bits_per_type_id - GM;
|
||||
|
||||
if constexpr (InitializeContext::template has_option<trace>) {
|
||||
ctx.tr << " Using " << num_groups << " groups for " << table_size << " keys\n";
|
||||
}
|
||||
|
||||
// Try different pilot hash parameters
|
||||
for (std::size_t pass = 0; pass < MAX_PASSES && total_attempts < MAX_ATTEMPTS; ++pass) {
|
||||
mult = uniform_dist(rnd) | 1;
|
||||
// Use a smaller multiplier for group hash to avoid overflow
|
||||
// We only need enough bits to distinguish between num_groups
|
||||
std::uniform_int_distribution<std::uint32_t> group_dist;
|
||||
group_mult = group_dist(rnd) | 1;
|
||||
|
||||
// Calculate M for pilot hash (number of bits for table_size range)
|
||||
std::size_t M = 0;
|
||||
power = 1;
|
||||
while (power < table_size * DISTRIBUTION_FACTOR) {
|
||||
power <<= 1;
|
||||
++M;
|
||||
}
|
||||
shift = bits_per_type_id - M;
|
||||
|
||||
// Partition keys into groups
|
||||
std::vector<std::vector<type_id>> groups(num_groups);
|
||||
for (auto key : keys) {
|
||||
auto group_idx = ((group_mult * reinterpret_cast<uintptr>(key)) >> group_shift) % num_groups;
|
||||
groups[group_idx].push_back(key);
|
||||
}
|
||||
|
||||
// Try to find displacements for each group
|
||||
detail::minimal_perfect_hash_displacements<Registry>.assign(num_groups, 0);
|
||||
buckets.assign(table_size, type_id(uintptr_max));
|
||||
std::vector<bool> used(table_size, false);
|
||||
bool success = true;
|
||||
|
||||
// Process groups in descending order of size (larger groups first)
|
||||
std::vector<std::size_t> group_order(num_groups);
|
||||
for (std::size_t i = 0; i < num_groups; ++i) group_order[i] = i;
|
||||
std::sort(group_order.begin(), group_order.end(),
|
||||
[&groups](std::size_t a, std::size_t b) {
|
||||
return groups[a].size() > groups[b].size();
|
||||
});
|
||||
|
||||
for (auto g : group_order) {
|
||||
if (groups[g].empty()) continue;
|
||||
|
||||
// Try different displacement values
|
||||
bool found = false;
|
||||
for (std::size_t disp = 0; disp < table_size * DISTRIBUTION_FACTOR && !found; ++disp) {
|
||||
++total_attempts;
|
||||
if (total_attempts > MAX_ATTEMPTS) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if this displacement works for all keys in group
|
||||
std::vector<std::size_t> positions;
|
||||
positions.reserve(groups[g].size());
|
||||
bool valid = true;
|
||||
for (auto key : groups[g]) {
|
||||
auto pilot = (mult * reinterpret_cast<uintptr>(key)) >> shift;
|
||||
auto pos = (pilot + disp) % table_size;
|
||||
if (used[pos]) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
positions.push_back(pos);
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
// Mark positions as used and store keys
|
||||
detail::minimal_perfect_hash_displacements<Registry>[g] = disp;
|
||||
for (std::size_t i = 0; i < groups[g].size(); ++i) {
|
||||
used[positions[i]] = true;
|
||||
buckets[positions[i]] = groups[g][i];
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
// Count how many positions are used
|
||||
std::size_t used_count = 0;
|
||||
for (std::size_t i = 0; i < table_size; ++i) {
|
||||
if (uintptr(buckets[i]) != uintptr_max) {
|
||||
used_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Accept if we've placed all keys (allow up to 10% waste)
|
||||
if (used_count == keys.size()) {
|
||||
if constexpr (InitializeContext::template has_option<trace>) {
|
||||
ctx.tr << " Found minimal perfect hash after " << total_attempts
|
||||
<< " attempts; " << used_count << "/" << table_size
|
||||
<< " slots used\n";
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Failed to find minimal perfect hash
|
||||
search_error error;
|
||||
error.attempts = total_attempts;
|
||||
error.buckets = table_size;
|
||||
|
||||
if constexpr (Registry::has_error_handler) {
|
||||
Registry::error_handler::error(error);
|
||||
}
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
template<class Registry>
|
||||
void minimal_perfect_hash::fn<Registry>::check(std::size_t index, type_id type) {
|
||||
if (index >= table_size ||
|
||||
detail::minimal_perfect_hash_control<Registry>[index] != type) {
|
||||
|
||||
if constexpr (Registry::has_error_handler) {
|
||||
missing_class error;
|
||||
error.type = type;
|
||||
Registry::error_handler::error(error);
|
||||
}
|
||||
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
template<class Registry, class Stream>
|
||||
auto minimal_perfect_hash::search_error::write(Stream& os) const -> void {
|
||||
os << "could not find minimal perfect hash factors after " << attempts
|
||||
<< " attempts using " << buckets << " buckets\n";
|
||||
}
|
||||
|
||||
} // namespace policies
|
||||
} // namespace boost::openmethod
|
||||
|
||||
#endif
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
// 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
|
||||
252
test/test_minimal_perfect_hash.cpp
Normal file
252
test/test_minimal_perfect_hash.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
// 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 <string>
|
||||
#include <set>
|
||||
|
||||
#define BOOST_TEST_MODULE minimal_perfect_hash
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <boost/openmethod.hpp>
|
||||
#include <boost/openmethod/policies/minimal_perfect_hash.hpp>
|
||||
#include <boost/openmethod/policies/std_rtti.hpp>
|
||||
#include <boost/openmethod/policies/vptr_vector.hpp>
|
||||
#include <boost/openmethod/policies/stderr_output.hpp>
|
||||
#include <boost/openmethod/policies/default_error_handler.hpp>
|
||||
#include <boost/openmethod/initialize.hpp>
|
||||
|
||||
#include "test_util.hpp"
|
||||
|
||||
using namespace boost::openmethod;
|
||||
using namespace boost::openmethod::policies;
|
||||
|
||||
// Test registry with minimal_perfect_hash
|
||||
struct minimal_hash_registry
|
||||
: registry<
|
||||
std_rtti, vptr_vector, minimal_perfect_hash,
|
||||
default_error_handler, stderr_output> {
|
||||
};
|
||||
|
||||
// Test registry with runtime checks
|
||||
struct minimal_hash_registry_with_checks
|
||||
: registry<
|
||||
std_rtti, vptr_vector, minimal_perfect_hash,
|
||||
default_error_handler, stderr_output, runtime_checks> {
|
||||
};
|
||||
|
||||
namespace test_basic {
|
||||
|
||||
struct Animal {
|
||||
virtual ~Animal() {}
|
||||
};
|
||||
|
||||
struct Dog : Animal {};
|
||||
struct Cat : Animal {};
|
||||
struct Bird : Animal {};
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, Bird, minimal_hash_registry);
|
||||
|
||||
BOOST_OPENMETHOD(get_sound, (virtual_<const Animal&>), std::string, minimal_hash_registry);
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(get_sound, (const Dog&), std::string) {
|
||||
return "woof";
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(get_sound, (const Cat&), std::string) {
|
||||
return "meow";
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(get_sound, (const Bird&), std::string) {
|
||||
return "chirp";
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(basic_functionality) {
|
||||
initialize<minimal_hash_registry>();
|
||||
|
||||
Dog dog;
|
||||
Cat cat;
|
||||
Bird bird;
|
||||
|
||||
BOOST_TEST(get_sound(dog) == "woof");
|
||||
BOOST_TEST(get_sound(cat) == "meow");
|
||||
BOOST_TEST(get_sound(bird) == "chirp");
|
||||
}
|
||||
|
||||
} // namespace test_basic
|
||||
|
||||
namespace test_hash_properties {
|
||||
|
||||
struct Base {
|
||||
virtual ~Base() {}
|
||||
};
|
||||
|
||||
struct D1 : Base {};
|
||||
struct D2 : Base {};
|
||||
struct D3 : Base {};
|
||||
struct D4 : Base {};
|
||||
struct D5 : Base {};
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Base, D1, D2, D3, D4, D5, minimal_hash_registry);
|
||||
|
||||
BOOST_OPENMETHOD(get_id, (virtual_<const Base&>), int, minimal_hash_registry);
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(get_id, (const D1&), int) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(get_id, (const D2&), int) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(get_id, (const D3&), int) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(get_id, (const D4&), int) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(get_id, (const D5&), int) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(minimal_hash_properties) {
|
||||
initialize<minimal_hash_registry>();
|
||||
|
||||
// Test that all classes are correctly hashed
|
||||
D1 d1;
|
||||
D2 d2;
|
||||
D3 d3;
|
||||
D4 d4;
|
||||
D5 d5;
|
||||
|
||||
BOOST_TEST(get_id(d1) == 1);
|
||||
BOOST_TEST(get_id(d2) == 2);
|
||||
BOOST_TEST(get_id(d3) == 3);
|
||||
BOOST_TEST(get_id(d4) == 4);
|
||||
BOOST_TEST(get_id(d5) == 5);
|
||||
|
||||
// Verify that the hash function produces a minimal perfect hash
|
||||
// (This is implicit - if it didn't, initialization would fail or we'd get wrong results)
|
||||
}
|
||||
|
||||
} // namespace test_hash_properties
|
||||
|
||||
namespace test_with_runtime_checks {
|
||||
|
||||
struct Vehicle {
|
||||
virtual ~Vehicle() {}
|
||||
};
|
||||
|
||||
struct Car : Vehicle {};
|
||||
struct Bike : Vehicle {};
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Vehicle, Car, Bike, minimal_hash_registry_with_checks);
|
||||
|
||||
BOOST_OPENMETHOD(get_wheels, (virtual_<const Vehicle&>), int, minimal_hash_registry_with_checks);
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(get_wheels, (const Car&), int) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(get_wheels, (const Bike&), int) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(runtime_checks) {
|
||||
initialize<minimal_hash_registry_with_checks>();
|
||||
|
||||
Car car;
|
||||
Bike bike;
|
||||
|
||||
BOOST_TEST(get_wheels(car) == 4);
|
||||
BOOST_TEST(get_wheels(bike) == 2);
|
||||
}
|
||||
|
||||
} // namespace test_with_runtime_checks
|
||||
|
||||
namespace test_empty {
|
||||
|
||||
struct Empty {
|
||||
virtual ~Empty() {}
|
||||
};
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Empty, minimal_hash_registry);
|
||||
|
||||
BOOST_OPENMETHOD(process, (virtual_<const Empty&>), int, minimal_hash_registry);
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(process, (const Empty&), int) {
|
||||
return 42;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(single_class) {
|
||||
initialize<minimal_hash_registry>();
|
||||
|
||||
Empty e;
|
||||
BOOST_TEST(process(e) == 42);
|
||||
}
|
||||
|
||||
} // namespace test_empty
|
||||
|
||||
namespace test_large_hierarchy {
|
||||
|
||||
struct Root {
|
||||
virtual ~Root() {}
|
||||
};
|
||||
|
||||
struct L1_1 : Root {};
|
||||
struct L1_2 : Root {};
|
||||
struct L1_3 : Root {};
|
||||
struct L1_4 : Root {};
|
||||
struct L1_5 : Root {};
|
||||
struct L1_6 : Root {};
|
||||
struct L1_7 : Root {};
|
||||
struct L1_8 : Root {};
|
||||
struct L1_9 : Root {};
|
||||
struct L1_10 : Root {};
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Root, L1_1, L1_2, L1_3, L1_4, L1_5, L1_6, L1_7, L1_8, L1_9, L1_10, minimal_hash_registry);
|
||||
|
||||
BOOST_OPENMETHOD(classify, (virtual_<const Root&>), int, minimal_hash_registry);
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(classify, (const L1_1&), int) { return 1; }
|
||||
BOOST_OPENMETHOD_OVERRIDE(classify, (const L1_2&), int) { return 2; }
|
||||
BOOST_OPENMETHOD_OVERRIDE(classify, (const L1_3&), int) { return 3; }
|
||||
BOOST_OPENMETHOD_OVERRIDE(classify, (const L1_4&), int) { return 4; }
|
||||
BOOST_OPENMETHOD_OVERRIDE(classify, (const L1_5&), int) { return 5; }
|
||||
BOOST_OPENMETHOD_OVERRIDE(classify, (const L1_6&), int) { return 6; }
|
||||
BOOST_OPENMETHOD_OVERRIDE(classify, (const L1_7&), int) { return 7; }
|
||||
BOOST_OPENMETHOD_OVERRIDE(classify, (const L1_8&), int) { return 8; }
|
||||
BOOST_OPENMETHOD_OVERRIDE(classify, (const L1_9&), int) { return 9; }
|
||||
BOOST_OPENMETHOD_OVERRIDE(classify, (const L1_10&), int) { return 10; }
|
||||
|
||||
BOOST_AUTO_TEST_CASE(larger_hierarchy) {
|
||||
initialize<minimal_hash_registry>();
|
||||
|
||||
L1_1 o1;
|
||||
L1_2 o2;
|
||||
L1_3 o3;
|
||||
L1_4 o4;
|
||||
L1_5 o5;
|
||||
L1_6 o6;
|
||||
L1_7 o7;
|
||||
L1_8 o8;
|
||||
L1_9 o9;
|
||||
L1_10 o10;
|
||||
|
||||
BOOST_TEST(classify(o1) == 1);
|
||||
BOOST_TEST(classify(o2) == 2);
|
||||
BOOST_TEST(classify(o3) == 3);
|
||||
BOOST_TEST(classify(o4) == 4);
|
||||
BOOST_TEST(classify(o5) == 5);
|
||||
BOOST_TEST(classify(o6) == 6);
|
||||
BOOST_TEST(classify(o7) == 7);
|
||||
BOOST_TEST(classify(o8) == 8);
|
||||
BOOST_TEST(classify(o9) == 9);
|
||||
BOOST_TEST(classify(o10) == 10);
|
||||
}
|
||||
|
||||
} // namespace test_large_hierarchy
|
||||
@@ -1,443 +0,0 @@
|
||||
// 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>;
|
||||
@@ -6,16 +6,10 @@
|
||||
#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/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/initialize.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"
|
||||
|
||||
@@ -25,7 +19,7 @@ using namespace boost::openmethod;
|
||||
using namespace policies;
|
||||
using namespace detail;
|
||||
|
||||
struct Animal : boost::intrusive_ref_counter<Animal> {
|
||||
struct Animal {
|
||||
virtual ~Animal() {
|
||||
}
|
||||
|
||||
@@ -37,12 +31,6 @@ 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>;
|
||||
|
||||
Reference in New Issue
Block a user