Skip to content

Commit

Permalink
vptr -> vptr_placement
Browse files Browse the repository at this point in the history
  • Loading branch information
jll63 committed Feb 21, 2024
1 parent 29bec1a commit 36dc147
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 38 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
2 changes: 1 addition & 1 deletion include/yorel/yomm2/policy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ void fast_perfect_hash<Policy>::hash_initialize(
auto type = *iter;
auto index = (type * mult) >> shift;

if (index > hash_last) {
if (index >= hash_last) {
hash_last = index + 1;
}

Expand Down
133 changes: 133 additions & 0 deletions reference.in/checked_perfect_hash.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#define BOOST_TEST_MODULE checked_perfect_hash
#include <boost/test/included/unit_test.hpp>

/***
<sub>/ ->home / ->reference </sub>
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<class Policy>
template <typename ForwardIterator>
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<class Policy>
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 <yorel/yomm2/policy.hpp>

// for brevity
using namespace yorel::yomm2;
using namespace yorel::yomm2::policy;

struct checked_policy
: basic_policy<checked_policy, checked_perfect_hash<checked_policy>,
throw_error_handler> {};

BOOST_AUTO_TEST_CASE(ref_check_checked_perfect_hash) {
std::vector<type_id> 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);
}
//***
32 changes: 17 additions & 15 deletions reference.in/vptr.cpp → reference.in/vptr_placement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@
/***
<sub>/ ->home / ->reference </sub>
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
Expand All @@ -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<Class>` 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:
| | |
| ----------------------------- | ----------------------------------------------- |
Expand All @@ -56,7 +58,7 @@ struct vptr_facet { template<class Class> 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:
| | |
Expand Down Expand Up @@ -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.
Expand All @@ -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<vptr> {
struct vptr_page : virtual default_static_policy::use_facet<vptr_placement> {
template<class Class>
static auto dynamic_vptr(Class& arg) {
if constexpr (std::is_base_of_v<Number, std::remove_const_t<Class>>) {
auto page = reinterpret_cast<std::uintptr_t>(&arg) & ~1023;
return *reinterpret_cast<std::uintptr_t**>(page);
} else {
return default_static_policy::use_facet<vptr>::dynamic_vptr(arg);
return default_static_policy::use_facet<vptr_placement>::dynamic_vptr(arg);
}
}
};

struct number_aware_policy : default_static_policy::replace<vptr, vptr_page> {};
struct number_aware_policy : default_static_policy::replace<vptr_placement, vptr_page> {};

// Make it the default policy.
#define YOMM2_DEFAULT_POLICY number_aware_policy
Expand Down
2 changes: 1 addition & 1 deletion reference.in/vptr_vector.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion reference/error.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<sub>/ [home](/reference//README.md) / [reference](/reference//reference/README.md) </sub>

**yorel::yomm2::policy::vptr**<br>
**yorel::yomm2::policy::error_handler**<br>
<sub>defined in <yorel/yomm2/core.hpp>, also provided by<yorel/yomm2/keywords.hpp></sub>

---
Expand Down
2 changes: 1 addition & 1 deletion reference/output.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<sub>/ [home](/reference//README.md) / [reference](/reference//reference/README.md) </sub>

**yorel::yomm2::policy::vptr**<br>
**yorel::yomm2::policy::error_output**<br>
<sub>defined in <yorel/yomm2/core.hpp>, also provided by<yorel/yomm2/keywords.hpp></sub>

---
Expand Down
2 changes: 1 addition & 1 deletion reference/policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
2 changes: 1 addition & 1 deletion reference/rtti.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<sub>/ [home](/reference//README.md) / [reference](/reference//reference/README.md) </sub>

**yorel::yomm2::policy::vptr**<br>
**yorel::yomm2::policy::rtti**<br>
<sub>defined in <yorel/yomm2/policy.hpp>, also provided by<yorel/yomm2/core.hpp>, <yorel/yomm2/keywords.hpp></sub>

---
Expand Down
32 changes: 17 additions & 15 deletions reference/vptr.md → reference/vptr_placement.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@

<sub>/ [home](/reference//README.md) / [reference](/reference//reference/README.md) </sub>

**yorel::yomm2::policy::vptr entry: yorel::yomm2::policy::external_vptr**<br>
**yorel::yomm2::policy::vptr_placement**<br>
**yorel::yomm2::policy::external_vptr**<br>

<sub>defined in <yorel/yomm2/policy.hpp>, also provided by<yorel/yomm2/keywords.hpp></sub>

---
```
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
Expand All @@ -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<Class>` 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:

| | |
| ----------------------------- | ----------------------------------------------- |
Expand All @@ -53,7 +55,7 @@ struct vptr_facet { template<class Class> 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:
| | |
Expand Down Expand Up @@ -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.
Expand All @@ -131,19 +133,19 @@ multi-methods.
using namespace yorel::yomm2;
using namespace policy;
struct vptr_page : virtual default_static_policy::use_facet<vptr> {
struct vptr_page : virtual default_static_policy::use_facet<vptr_placement> {
template<class Class>
static auto dynamic_vptr(Class& arg) {
if constexpr (std::is_base_of_v<Number, std::remove_const_t<Class>>) {
auto page = reinterpret_cast<std::uintptr_t>(&arg) & ~1023;
return *reinterpret_cast<std::uintptr_t**>(page);
} else {
return default_static_policy::use_facet<vptr>::dynamic_vptr(arg);
return default_static_policy::use_facet<vptr_placement>::dynamic_vptr(arg);
}
}
};
struct number_aware_policy : default_static_policy::replace<vptr, vptr_page> {};
struct number_aware_policy : default_static_policy::replace<vptr_placement, vptr_page> {};
// Make it the default policy.
#define YOMM2_DEFAULT_POLICY number_aware_policy
Expand Down
2 changes: 1 addition & 1 deletion reference/vptr_vector.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit 36dc147

Please sign in to comment.