diff --git a/CMakeLists.txt b/CMakeLists.txt index ac505332..5ed2cbf4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ include(cmake/find_or_download_package.cmake) find_or_download_package(Boost INSTALL_WITH_YOMM) message(STATUS "Using Boost libraries from ${Boost_INCLUDE_DIRS}") -if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") +if(NOT ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") AND (CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")) add_compile_options(-save-temps -fverbose-asm -masm=intel) endif() diff --git a/include/yorel/yomm2/policy.hpp b/include/yorel/yomm2/policy.hpp index ba6f764e..cc910888 100644 --- a/include/yorel/yomm2/policy.hpp +++ b/include/yorel/yomm2/policy.hpp @@ -481,7 +481,7 @@ void fast_perfect_hash::hash_initialize( auto type = *iter; auto index = (type * mult) >> shift; - if (index > hash_last) { + if (index >= hash_last) { hash_last = index + 1; } diff --git a/reference.in/checked_perfect_hash.cpp b/reference.in/checked_perfect_hash.cpp index e69de29b..e323d008 100644 --- a/reference.in/checked_perfect_hash.cpp +++ b/reference.in/checked_perfect_hash.cpp @@ -0,0 +1,133 @@ +#define BOOST_TEST_MODULE checked_perfect_hash +#include + +/*** + +/ ->home / ->reference + +entry: yorel::yomm2::policy::checked_perfect_hash +headers: yorel/yomm2/policy.hpp, yorel/yomm2/core.hpp, yorel/yomm2/keywords.hpp + +--- +``` +struct checked_perfect_hash; +``` + +`checked_perfect_hash` is an implementation of ->`type_hash`. It uses the following +hash function: + +``` +H(x) = (x * M) >> S +``` + +The output values are in the interval `[0, 1 << (N - S)[`, where `N` is the +bit size of `type_id`. + +The `M` and `S` coefficients are determined during initialization so that, for +the given set of type ids, the hash function is _perfect_, i.e. collision-free. +It is not _minimal_: there may be gaps in the output interval. + +`hash_type_id` must not be called with ids not present in the input set passed +to `hash_initialize`. This happens when the class of a virtual argument was not +registered via ->`use_classes` or ->`register_classes`. `ref_perfect_hash` will +silently return an index in the output range, to be used to fetch the v-table +for the class from a vector. From there, two things can happen. In the lucky +scenario, the index will correspond to a gap, a wasted entry in the v-table +vector, which will contain a null pointer. The program will segfault. Such +mistake is fairly easy to troubleshoot. In the unlucky scenario, there will be a +collision - the hash function is perfect only for the input ids - and the wrong +method will be called. The program will carry on, crashing far from the root +cause of the error, and, in the worst case, not at all. + +To help detect such registration errors, it is highly recommended to run debug +builds of the program with ->`checked_perfect_hash`, which detects missing class +registrations. + +`default_policy` uses `checked_perfect_hash` in release builds, and +`checked_perfect_hash` in debug builds. + +## Template parameters + +**Policy** - the policy containing the facet. + +## static member functions +| | | +| ----------------------------------- | ---------------------------- | +| [hash_initialize](#hash_initialize) | finds a hash function | +| [hash_type_id](#hash_type_id) | returns the hashed `type_id` | + +### hash_initialize + +```c++ +template +template +static size_t hash_initialize(ForwardIterator first, ForwardIterator last) +``` + +Finds the `M` and `S` parameters of the hash function. `ForwardIterator` is an +iterator that satisfies the requirements of forward iterators; dereferencing it +yields a `type_id`. Sets `type_hash_last` to the maximum hash value, plus one. + +#### Parameters + +**first**, **last** - a range of `type_id`s + +#### Return value + +None. + +#### Errors + +None. + +### hash_type_id + +```c++ +template +static type_id hash_type_id(type_id type) +``` + +Returns the hashed value of `type`, which must be one of the values passed to +`hash_initialize`. + +#### Parameters + +**type** - a `type_id` + +#### Return value + +None. + +#### Errors + +None. + +### Example + +***/ + +//*** +#include + +// for brevity +using namespace yorel::yomm2; +using namespace yorel::yomm2::policy; + +struct checked_policy + : basic_policy, + throw_error_handler> {}; + +BOOST_AUTO_TEST_CASE(ref_check_checked_perfect_hash) { + std::vector ids = {42, 1963, 602701}; + checked_policy::hash_initialize(ids.begin(), ids.end()); + bool caught = false; + + try { + checked_policy::hash_type_id(666); + } catch (unknown_class_error &error) { + caught = true; + } + + BOOST_TEST(caught); +} +//*** diff --git a/reference.in/vptr.cpp b/reference.in/vptr_placement.cpp similarity index 89% rename from reference.in/vptr.cpp rename to reference.in/vptr_placement.cpp index b4ba74f5..05686e9c 100644 --- a/reference.in/vptr.cpp +++ b/reference.in/vptr_placement.cpp @@ -4,17 +4,19 @@ /*** / ->home / ->reference -entry: yorel::yomm2::policy::vptr entry: yorel::yomm2::policy::external_vptr +entry: yorel::yomm2::policy::vptr_placement +entry: yorel::yomm2::policy::external_vptr hrefs: external_vptr headers: yorel/yomm2/policy.hpp, yorel/yomm2/keywords.hpp --- ``` -struct vptr {}; struct external_vptr : virtual vptr {}; +struct vptr_placement; +struct external_vptr; ``` --- -The `vptr` facet is responsible for retrieving a pointer to the v-table for an +The `vptr_placement` facet is responsible for retrieving a pointer to the v-table for an object. YOMM2 implements method dispatch in a way similar to native virtual function @@ -24,23 +26,23 @@ contain pointers to functions for unary methods, and, for multi-methods, pointers to, and coordinates in, a multi-dimensional table of pointers to functions. -The `vptr` facet is used during method call to fetch the vptr for virtual +The `vptr_placement` facet is used during method call to fetch the vptr_placement for virtual arguments corresponding to the `virtual_` parameters in the method declaration. It is also used by the constructor of `virtual_ptr` to obtain a -vptr on the basis of an object's dynamic type. +vptr_placement on the basis of an object's dynamic type. `virtual_ptr::final`, and the related convenience functions, assume that the -static and dynamic types of their argument are the same. The vptr is obtained +static and dynamic types of their argument are the same. The vptr_placement is obtained statically from the policy's `static_vptr` member. It is conceivable -to organize an entire program around the "final" constructs; thus, the `vptr` +to organize an entire program around the "final" constructs; thus, the `vptr_placement` facet is optional. `external_vptr` is a sub-category of `facet`. If present, it provides a `register_vptrs` function, called by `update`. -### Requirements for implementations of `vptr` +### Requirements for implementations of `vptr_placement` -An implementation of `vptr` must provide the following static function template: +An implementation of `vptr_placement` must provide the following static function template: | | | | ----------------------------- | ----------------------------------------------- | @@ -56,7 +58,7 @@ struct vptr_facet { template static const std::uintptr_t* ### Requirements for implementations of `external_vptr` -In addition to the requirements for `vptr`, an implementation of `external_vptr` +In addition to the requirements for `vptr_placement`, an implementation of `external_vptr` must provide the following static function template: | | | @@ -120,9 +122,9 @@ the pointer to the v-table at the beginning of each page. It is thus shared by a large number of objects. To make it easy to find the beginning of the page, we allocate the pages on a 1024 byte boundary (or some other power of two). -For this, we create a new `vptr` facet, which checks if the object is derived +For this, we create a new `vptr_placement` facet, which checks if the object is derived from `Number`. If yes, it locates the base of the page (`&obj & ~1023`) and -returns the vptr stored there. Otherwise, it falls back to the default behavior, +returns the vptr_placement stored there. Otherwise, it falls back to the default behavior, so we can have both `Number` and normal, polymorphic classes as parameters in multi-methods. @@ -149,19 +151,19 @@ void* aligned_alloc(size_t alignment, size_t size) { using namespace yorel::yomm2; using namespace policy; -struct vptr_page : virtual default_static_policy::use_facet { +struct vptr_page : virtual default_static_policy::use_facet { template static auto dynamic_vptr(Class& arg) { if constexpr (std::is_base_of_v>) { auto page = reinterpret_cast(&arg) & ~1023; return *reinterpret_cast(page); } else { - return default_static_policy::use_facet::dynamic_vptr(arg); + return default_static_policy::use_facet::dynamic_vptr(arg); } } }; -struct number_aware_policy : default_static_policy::replace {}; +struct number_aware_policy : default_static_policy::replace {}; // Make it the default policy. #define YOMM2_DEFAULT_POLICY number_aware_policy diff --git a/reference.in/vptr_vector.md b/reference.in/vptr_vector.md index d6cb6aad..a372cde1 100644 --- a/reference.in/vptr_vector.md +++ b/reference.in/vptr_vector.md @@ -10,7 +10,7 @@ struct vptr_vector : virtual external_vptr { ... }; ``` --- -`vptr_vector` is an implementation of [`external_vptr`](vptr.md) that stores the +`vptr_vector` is an implementation of [`external_vptr`](vptr_placement.md) that stores the pointers to the v-tables in a `std::vector`. If the policy contains a ->`type_hash` facet, it is used to convert the ->`type_id` to an index in the vector; otherwise, the `type_id` is used as the index. diff --git a/reference/error.md b/reference/error.md index ec4608e9..36bfc26c 100644 --- a/reference/error.md +++ b/reference/error.md @@ -2,7 +2,7 @@ / [home](/reference//README.md) / [reference](/reference//reference/README.md) -**yorel::yomm2::policy::vptr**
+**yorel::yomm2::policy::error_handler**
defined in , also provided by --- diff --git a/reference/output.md b/reference/output.md index 1057e73d..c3a79deb 100644 --- a/reference/output.md +++ b/reference/output.md @@ -2,7 +2,7 @@ / [home](/reference//README.md) / [reference](/reference//reference/README.md) -**yorel::yomm2::policy::vptr**
+**yorel::yomm2::policy::error_output**
defined in , also provided by --- diff --git a/reference/policy.md b/reference/policy.md index 77535f4d..1a528b0f 100644 --- a/reference/policy.md +++ b/reference/policy.md @@ -37,7 +37,7 @@ implementation for each category. They are summed up in the following table. | facet category | responsibility | implementations | | ----------------------- | ------------------------------- | ------------------------------------------------------------------- | -| vptr, external_vptr | fetch vptr for virtual argument | **vptr_vector\<...>** (D, R), vptr_map\<...> | +| vptr_placement, external_vptr | fetch vptr_placement for virtual argument | **vptr_vector\<...>** (D, R), vptr_map\<...> | | **rtti**, deferred_rtti | type information | **std_rtti** (D, R), final_only_rtti | | type_hash | map type info to integer index | **fast_perfect_hash\<...>** (R), **checked_perfect_hash`<...>** (D) | | error_handler | report errors | vectored_error_handler\<...>, throw_error_handler | diff --git a/reference/rtti.md b/reference/rtti.md index 1669988a..6f0b5ff4 100644 --- a/reference/rtti.md +++ b/reference/rtti.md @@ -2,7 +2,7 @@ / [home](/reference//README.md) / [reference](/reference//reference/README.md) -**yorel::yomm2::policy::vptr**
+**yorel::yomm2::policy::rtti**
defined in , also provided by, --- diff --git a/reference/vptr.md b/reference/vptr_placement.md similarity index 89% rename from reference/vptr.md rename to reference/vptr_placement.md index 4b67f089..ce90b07f 100644 --- a/reference/vptr.md +++ b/reference/vptr_placement.md @@ -1,17 +1,19 @@ / [home](/reference//README.md) / [reference](/reference//reference/README.md) -**yorel::yomm2::policy::vptr entry: yorel::yomm2::policy::external_vptr**
+**yorel::yomm2::policy::vptr_placement**
+**yorel::yomm2::policy::external_vptr**
defined in , also provided by --- ``` -struct vptr {}; struct external_vptr : virtual vptr {}; +struct vptr_placement; +struct external_vptr; ``` --- -The `vptr` facet is responsible for retrieving a pointer to the v-table for an +The `vptr_placement` facet is responsible for retrieving a pointer to the v-table for an object. YOMM2 implements method dispatch in a way similar to native virtual function @@ -21,23 +23,23 @@ contain pointers to functions for unary methods, and, for multi-methods, pointers to, and coordinates in, a multi-dimensional table of pointers to functions. -The `vptr` facet is used during method call to fetch the vptr for virtual +The `vptr_placement` facet is used during method call to fetch the vptr_placement for virtual arguments corresponding to the `virtual_` parameters in the method declaration. It is also used by the constructor of `virtual_ptr` to obtain a -vptr on the basis of an object's dynamic type. +vptr_placement on the basis of an object's dynamic type. `virtual_ptr::final`, and the related convenience functions, assume that the -static and dynamic types of their argument are the same. The vptr is obtained +static and dynamic types of their argument are the same. The vptr_placement is obtained statically from the policy's `static_vptr` member. It is conceivable -to organize an entire program around the "final" constructs; thus, the `vptr` +to organize an entire program around the "final" constructs; thus, the `vptr_placement` facet is optional. `external_vptr` is a sub-category of `facet`. If present, it provides a `register_vptrs` function, called by `update`. -### Requirements for implementations of `vptr` +### Requirements for implementations of `vptr_placement` -An implementation of `vptr` must provide the following static function template: +An implementation of `vptr_placement` must provide the following static function template: | | | | ----------------------------- | ----------------------------------------------- | @@ -53,7 +55,7 @@ struct vptr_facet { template static const std::uintptr_t* ### Requirements for implementations of `external_vptr` -In addition to the requirements for `vptr`, an implementation of `external_vptr` +In addition to the requirements for `vptr_placement`, an implementation of `external_vptr` must provide the following static function template: | | | @@ -115,9 +117,9 @@ the pointer to the v-table at the beginning of each page. It is thus shared by a large number of objects. To make it easy to find the beginning of the page, we allocate the pages on a 1024 byte boundary (or some other power of two). -For this, we create a new `vptr` facet, which checks if the object is derived +For this, we create a new `vptr_placement` facet, which checks if the object is derived from `Number`. If yes, it locates the base of the page (`&obj & ~1023`) and -returns the vptr stored there. Otherwise, it falls back to the default behavior, +returns the vptr_placement stored there. Otherwise, it falls back to the default behavior, so we can have both `Number` and normal, polymorphic classes as parameters in multi-methods. @@ -131,19 +133,19 @@ multi-methods. using namespace yorel::yomm2; using namespace policy; -struct vptr_page : virtual default_static_policy::use_facet { +struct vptr_page : virtual default_static_policy::use_facet { template static auto dynamic_vptr(Class& arg) { if constexpr (std::is_base_of_v>) { auto page = reinterpret_cast(&arg) & ~1023; return *reinterpret_cast(page); } else { - return default_static_policy::use_facet::dynamic_vptr(arg); + return default_static_policy::use_facet::dynamic_vptr(arg); } } }; -struct number_aware_policy : default_static_policy::replace {}; +struct number_aware_policy : default_static_policy::replace {}; // Make it the default policy. #define YOMM2_DEFAULT_POLICY number_aware_policy diff --git a/reference/vptr_vector.md b/reference/vptr_vector.md index a098d3c3..d98be61d 100644 --- a/reference/vptr_vector.md +++ b/reference/vptr_vector.md @@ -10,7 +10,7 @@ struct vptr_vector : virtual external_vptr { ... }; ``` --- -`vptr_vector` is an implementation of [`external_vptr`](vptr.md) that stores the +`vptr_vector` is an implementation of [`external_vptr`](vptr_placement.md) that stores the pointers to the v-tables in a `std::vector`. If the policy contains a [`type_hash`](/reference/type_hash.md) facet, it is used to convert the [`type_id`](/reference/type_id.md) to an index in the vector; otherwise, the `type_id` is used as the index.