dxx-rebirth/common/include/valptridx.h

1171 lines
42 KiB
C
Raw Normal View History

2014-06-01 17:55:23 +00:00
/*
* This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
2014-06-01 17:55:23 +00:00
* It is copyright by its individual contributors, as recorded in the
* project's Git history. See COPYING.txt at the top level for license
* terms and a link to the Git history.
*/
#pragma once
#include <stdexcept>
2015-05-09 17:39:02 +00:00
#include <string>
#include <type_traits>
#include "fwd-valptridx.h"
2014-12-14 05:23:00 +00:00
#include "pack.h"
2017-02-19 19:33:36 +00:00
#include "compiler-poison.h"
#include "selfiter.h"
#ifdef DXX_CONSTANT_TRUE
#ifdef DXX_HAVE_ATTRIBUTE_WARNING
/* This causes many warnings because some conversions are not checked for
* safety. Eliminating the warnings by changing the call sites to check first
* would be a useful improvement.
*/
//#define DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT __attribute__((__warning__("call not eliminated")))
#endif
#endif
Remove DXX_VALPTRIDX_STATIC_CHECK clang-14 is detected as being able to optimize out unreachable paths, but then triggers a build error reporting an unspecified invalid use somewhere in multi.cpp. Remove the static check, and rely on -Wsuggest-attribute=noreturn to report any functions which are guaranteed to fail. This is a weaker check, but over the course of development, the static check has been hit rarely, if ever, so keeping it provides little value. ``` In file included from similar/main/multi.cpp:38: In file included from common/main/game.h:32: In file included from common/main/robot.h:34: In file included from common/main/object.h:40: common/include/valptridx.h:229:2: error: call to unsigned int valptridx<dcx::player>::check_index_range_size<valptridx<dcx::player>::index_range_exception, std::__1::less>(char const*, unsigned int, unsigned long, valptridx<dcx::player>::array_managed_type const*)::DXX_ALWAYS_ERROR_FUNCTION::dxx_trap_handle_index_range_error() declared with 'error' attribute: invalid index used in array subscript DXX_VALPTRIDX_CHECK(Compare<std::size_t>()(s, array_size), handle_index_range_error, "invalid index used in array subscript", a, s); ^ common/include/valptridx.h:37:3: note: expanded from macro 'DXX_VALPTRIDX_CHECK' DXX_VALPTRIDX_STATIC_CHECK(dxx_valptridx_check_success_condition, dxx_trap_##ERROR, FAILURE_STRING); \ ^ common/include/valptridx.h:20:5: note: expanded from macro 'DXX_VALPTRIDX_STATIC_CHECK' (DXX_ALWAYS_ERROR_FUNCTION(FAILURE_FUNCTION, FAILURE_STRING), 0) \ ^ build/ulinux-clang++-14-64b10d04-ogl/dxxsconf.h:84:2: note: expanded from macro 'DXX_ALWAYS_ERROR_FUNCTION' DXX_ALWAYS_ERROR_FUNCTION::F(); \ ^ 1 error generated. ```
2022-08-22 01:24:49 +00:00
#define DXX_VALPTRIDX_CHECK(SUCCESS_CONDITION,ERROR,...) \
static_cast<void>( \
static_cast<bool>(SUCCESS_CONDITION) || (ERROR::report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VA(__VA_ARGS__)), 0) \
) \
#ifndef DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT
#define DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT
#endif
template <typename managed_type>
class valptridx<managed_type>::array_base_count_type
{
protected:
union {
unsigned count;
/*
* Use DXX_VALPTRIDX_FOR_EACH_PPI_TYPE to generate empty union
* members based on basic_{i,v}val_member_factory
* specializations.
*/
#define DXX_VALPTRIDX_DEFINE_MEMBER_FACTORIES(MANAGED_TYPE, DERIVED_TYPE_PREFIX, CONTEXT, PISUFFIX, IVPREFIX, MCPREFIX) \
valptridx<MANAGED_TYPE>::f ## IVPREFIX ## MCPREFIX ## PISUFFIX \
IVPREFIX ## MCPREFIX ## PISUFFIX
DXX_VALPTRIDX_FOR_EACH_PPI_TYPE(DXX_VALPTRIDX_DEFINE_MEMBER_FACTORIES, managed_type,,);
#undef DXX_VALPTRIDX_DEFINE_MEMBER_FACTORIES
};
constexpr array_base_count_type() :
count(0)
{
}
public:
unsigned get_count() const
{
return count;
}
void set_count(const unsigned c)
{
count = c;
}
};
template <
typename INTEGRAL_TYPE,
std::size_t array_size_value,
valptridx_detail::untyped_utilities::report_error_style report_const_error_value,
valptridx_detail::untyped_utilities::report_error_style report_mutable_error_value
>
constexpr std::integral_constant<std::size_t, array_size_value> valptridx_detail::specialized_type_parameters<INTEGRAL_TYPE, array_size_value, report_const_error_value, report_mutable_error_value>::array_size;
class valptridx_detail::untyped_utilities::report_error_undefined
{
public:
__attribute_cold
DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT
static void report(...)
{
}
};
class valptridx_detail::untyped_utilities::report_error_trap_terse
{
public:
/* Accept and discard any arguments, to encourage the compiler to discard
* as dead any values that exist only as arguments to `report()`.
*/
[[noreturn]]
__attribute_cold
DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT
static void report(...)
{
__builtin_trap();
}
};
class valptridx_detail::untyped_utilities::index_mismatch_trap_verbose
{
public:
[[noreturn]]
__attribute_cold
DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT
static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const void *const array, const unsigned long supplied_index, const void *const expected_pointer, const void *const actual_pointer)
{
/* Load each of the arguments into storage before executing the trap,
* so that inspection of those locations in the core dump can readily
* retrieve these values, even if the values would otherwise be unused
* and optimized out.
*/
__asm__ __volatile__("" :: DXX_VALPTRIDX_REPORT_STANDARD_ASM_LOAD_COMMA_R_VARS "rm" (array), "rm" (supplied_index), "rm" (expected_pointer), "rm" (actual_pointer));
__builtin_trap();
}
};
class valptridx_detail::untyped_utilities::index_range_trap_verbose
{
public:
[[noreturn]]
__attribute_cold
DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT
static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const void *const array, const unsigned long supplied_index)
{
__asm__ __volatile__("" :: DXX_VALPTRIDX_REPORT_STANDARD_ASM_LOAD_COMMA_R_VARS "rm" (array), "rm" (supplied_index));
__builtin_trap();
}
};
class valptridx_detail::untyped_utilities::null_pointer_trap_verbose
{
public:
[[noreturn]]
__attribute_cold
DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT
static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_DEFN_VARS)
{
__asm__ __volatile__("" :: DXX_VALPTRIDX_REPORT_STANDARD_ASM_LOAD_COMMA_N_VARS);
__builtin_trap();
}
[[noreturn]]
__attribute_cold
DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT
static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const void *const array)
{
__asm__ __volatile__("" :: DXX_VALPTRIDX_REPORT_STANDARD_ASM_LOAD_COMMA_R_VARS "rm" (array));
__builtin_trap();
}
};
template <typename P>
class valptridx<P>::index_mismatch_exception :
public std::logic_error
{
using std::logic_error::logic_error;
public:
[[noreturn]]
2015-05-09 17:39:02 +00:00
__attribute_cold
DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT
static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const array_managed_type *, index_type, const_pointer_type, const_pointer_type);
};
template <typename P>
class valptridx<P>::index_range_exception :
public std::out_of_range
{
using std::out_of_range::out_of_range;
public:
[[noreturn]]
2015-05-09 17:39:02 +00:00
__attribute_cold
DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT
static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const array_managed_type *, long);
};
template <typename P>
class valptridx<P>::null_pointer_exception :
public std::logic_error
{
using std::logic_error::logic_error;
public:
[[noreturn]]
2015-05-09 17:39:02 +00:00
__attribute_cold
DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT
static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_DEFN_VARS);
[[noreturn]]
2015-10-13 02:43:24 +00:00
__attribute_cold
DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT
static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const array_managed_type *);
2014-09-12 03:22:34 +00:00
};
template <typename managed_type>
template <typename handle_index_mismatch>
void valptridx<managed_type>::check_index_match(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const_reference_type r, index_type i, const array_managed_type &a __attribute_unused)
{
const auto pi = &a[i];
Remove DXX_VALPTRIDX_STATIC_CHECK clang-14 is detected as being able to optimize out unreachable paths, but then triggers a build error reporting an unspecified invalid use somewhere in multi.cpp. Remove the static check, and rely on -Wsuggest-attribute=noreturn to report any functions which are guaranteed to fail. This is a weaker check, but over the course of development, the static check has been hit rarely, if ever, so keeping it provides little value. ``` In file included from similar/main/multi.cpp:38: In file included from common/main/game.h:32: In file included from common/main/robot.h:34: In file included from common/main/object.h:40: common/include/valptridx.h:229:2: error: call to unsigned int valptridx<dcx::player>::check_index_range_size<valptridx<dcx::player>::index_range_exception, std::__1::less>(char const*, unsigned int, unsigned long, valptridx<dcx::player>::array_managed_type const*)::DXX_ALWAYS_ERROR_FUNCTION::dxx_trap_handle_index_range_error() declared with 'error' attribute: invalid index used in array subscript DXX_VALPTRIDX_CHECK(Compare<std::size_t>()(s, array_size), handle_index_range_error, "invalid index used in array subscript", a, s); ^ common/include/valptridx.h:37:3: note: expanded from macro 'DXX_VALPTRIDX_CHECK' DXX_VALPTRIDX_STATIC_CHECK(dxx_valptridx_check_success_condition, dxx_trap_##ERROR, FAILURE_STRING); \ ^ common/include/valptridx.h:20:5: note: expanded from macro 'DXX_VALPTRIDX_STATIC_CHECK' (DXX_ALWAYS_ERROR_FUNCTION(FAILURE_FUNCTION, FAILURE_STRING), 0) \ ^ build/ulinux-clang++-14-64b10d04-ogl/dxxsconf.h:84:2: note: expanded from macro 'DXX_ALWAYS_ERROR_FUNCTION' DXX_ALWAYS_ERROR_FUNCTION::F(); \ ^ 1 error generated. ```
2022-08-22 01:24:49 +00:00
DXX_VALPTRIDX_CHECK(pi == &r, handle_index_mismatch, &a, i, pi, &r);
}
2014-09-12 03:22:34 +00:00
template <typename managed_type>
template <typename handle_index_range_error, template <typename> class Compare>
typename valptridx<managed_type>::index_type valptridx<managed_type>::check_index_range(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const index_type i, const array_managed_type *const a)
{
2020-12-20 20:39:07 +00:00
std::size_t si;
if constexpr (std::is_enum<index_type>::value)
si = static_cast<std::size_t>(i);
else
si = i;
check_index_range_size<handle_index_range_error, Compare>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS si, a);
return i;
}
2014-09-12 03:22:34 +00:00
template <typename managed_type>
template <typename handle_index_range_error, template <typename> class Compare>
typename valptridx<managed_type>::index_type valptridx<managed_type>::check_index_range_size(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const std::size_t s, const array_managed_type *const a)
{
Remove DXX_VALPTRIDX_STATIC_CHECK clang-14 is detected as being able to optimize out unreachable paths, but then triggers a build error reporting an unspecified invalid use somewhere in multi.cpp. Remove the static check, and rely on -Wsuggest-attribute=noreturn to report any functions which are guaranteed to fail. This is a weaker check, but over the course of development, the static check has been hit rarely, if ever, so keeping it provides little value. ``` In file included from similar/main/multi.cpp:38: In file included from common/main/game.h:32: In file included from common/main/robot.h:34: In file included from common/main/object.h:40: common/include/valptridx.h:229:2: error: call to unsigned int valptridx<dcx::player>::check_index_range_size<valptridx<dcx::player>::index_range_exception, std::__1::less>(char const*, unsigned int, unsigned long, valptridx<dcx::player>::array_managed_type const*)::DXX_ALWAYS_ERROR_FUNCTION::dxx_trap_handle_index_range_error() declared with 'error' attribute: invalid index used in array subscript DXX_VALPTRIDX_CHECK(Compare<std::size_t>()(s, array_size), handle_index_range_error, "invalid index used in array subscript", a, s); ^ common/include/valptridx.h:37:3: note: expanded from macro 'DXX_VALPTRIDX_CHECK' DXX_VALPTRIDX_STATIC_CHECK(dxx_valptridx_check_success_condition, dxx_trap_##ERROR, FAILURE_STRING); \ ^ common/include/valptridx.h:20:5: note: expanded from macro 'DXX_VALPTRIDX_STATIC_CHECK' (DXX_ALWAYS_ERROR_FUNCTION(FAILURE_FUNCTION, FAILURE_STRING), 0) \ ^ build/ulinux-clang++-14-64b10d04-ogl/dxxsconf.h:84:2: note: expanded from macro 'DXX_ALWAYS_ERROR_FUNCTION' DXX_ALWAYS_ERROR_FUNCTION::F(); \ ^ 1 error generated. ```
2022-08-22 01:24:49 +00:00
DXX_VALPTRIDX_CHECK(Compare<std::size_t>()(s, array_size), handle_index_range_error, a, s);
/* This cast will truncate the value to fit in index_type, which is
* normally smaller than std::size_t. However, truncation is legal
* here, because (1) DXX_VALPTRIDX_CHECK would trap on an index that
* was outside the array size and (2) index_type can represent any
* valid size in the array. Thus, if DXX_VALPTRIDX_CHECK did not
* trap[1], the truncation cannot change the value of the index.
*
* [1] If valptridx was built without index validation, then no trap
* would be issued even for invalid data. Validation-disabled builds
* are permitted to exhibit undefined behavior in cases where the
* validation-enabled build would have trapped.
*/
return static_cast<index_type>(s);
}
template <typename managed_type>
template <typename handle_null_pointer>
void valptridx<managed_type>::check_null_pointer_conversion(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const_pointer_type p)
{
Remove DXX_VALPTRIDX_STATIC_CHECK clang-14 is detected as being able to optimize out unreachable paths, but then triggers a build error reporting an unspecified invalid use somewhere in multi.cpp. Remove the static check, and rely on -Wsuggest-attribute=noreturn to report any functions which are guaranteed to fail. This is a weaker check, but over the course of development, the static check has been hit rarely, if ever, so keeping it provides little value. ``` In file included from similar/main/multi.cpp:38: In file included from common/main/game.h:32: In file included from common/main/robot.h:34: In file included from common/main/object.h:40: common/include/valptridx.h:229:2: error: call to unsigned int valptridx<dcx::player>::check_index_range_size<valptridx<dcx::player>::index_range_exception, std::__1::less>(char const*, unsigned int, unsigned long, valptridx<dcx::player>::array_managed_type const*)::DXX_ALWAYS_ERROR_FUNCTION::dxx_trap_handle_index_range_error() declared with 'error' attribute: invalid index used in array subscript DXX_VALPTRIDX_CHECK(Compare<std::size_t>()(s, array_size), handle_index_range_error, "invalid index used in array subscript", a, s); ^ common/include/valptridx.h:37:3: note: expanded from macro 'DXX_VALPTRIDX_CHECK' DXX_VALPTRIDX_STATIC_CHECK(dxx_valptridx_check_success_condition, dxx_trap_##ERROR, FAILURE_STRING); \ ^ common/include/valptridx.h:20:5: note: expanded from macro 'DXX_VALPTRIDX_STATIC_CHECK' (DXX_ALWAYS_ERROR_FUNCTION(FAILURE_FUNCTION, FAILURE_STRING), 0) \ ^ build/ulinux-clang++-14-64b10d04-ogl/dxxsconf.h:84:2: note: expanded from macro 'DXX_ALWAYS_ERROR_FUNCTION' DXX_ALWAYS_ERROR_FUNCTION::F(); \ ^ 1 error generated. ```
2022-08-22 01:24:49 +00:00
DXX_VALPTRIDX_CHECK(p, handle_null_pointer);
}
template <typename managed_type>
template <typename handle_null_pointer>
void valptridx<managed_type>::check_null_pointer(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const_pointer_type p, const array_managed_type &a __attribute_unused)
{
Remove DXX_VALPTRIDX_STATIC_CHECK clang-14 is detected as being able to optimize out unreachable paths, but then triggers a build error reporting an unspecified invalid use somewhere in multi.cpp. Remove the static check, and rely on -Wsuggest-attribute=noreturn to report any functions which are guaranteed to fail. This is a weaker check, but over the course of development, the static check has been hit rarely, if ever, so keeping it provides little value. ``` In file included from similar/main/multi.cpp:38: In file included from common/main/game.h:32: In file included from common/main/robot.h:34: In file included from common/main/object.h:40: common/include/valptridx.h:229:2: error: call to unsigned int valptridx<dcx::player>::check_index_range_size<valptridx<dcx::player>::index_range_exception, std::__1::less>(char const*, unsigned int, unsigned long, valptridx<dcx::player>::array_managed_type const*)::DXX_ALWAYS_ERROR_FUNCTION::dxx_trap_handle_index_range_error() declared with 'error' attribute: invalid index used in array subscript DXX_VALPTRIDX_CHECK(Compare<std::size_t>()(s, array_size), handle_index_range_error, "invalid index used in array subscript", a, s); ^ common/include/valptridx.h:37:3: note: expanded from macro 'DXX_VALPTRIDX_CHECK' DXX_VALPTRIDX_STATIC_CHECK(dxx_valptridx_check_success_condition, dxx_trap_##ERROR, FAILURE_STRING); \ ^ common/include/valptridx.h:20:5: note: expanded from macro 'DXX_VALPTRIDX_STATIC_CHECK' (DXX_ALWAYS_ERROR_FUNCTION(FAILURE_FUNCTION, FAILURE_STRING), 0) \ ^ build/ulinux-clang++-14-64b10d04-ogl/dxxsconf.h:84:2: note: expanded from macro 'DXX_ALWAYS_ERROR_FUNCTION' DXX_ALWAYS_ERROR_FUNCTION::F(); \ ^ 1 error generated. ```
2022-08-22 01:24:49 +00:00
DXX_VALPTRIDX_CHECK(p, handle_null_pointer, &a);
}
2014-10-02 03:02:34 +00:00
template <typename managed_type>
template <typename handle_index_mismatch, typename handle_index_range_error>
void valptridx<managed_type>::check_implicit_index_range_ref(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const managed_type &r, const array_managed_type &a)
{
check_explicit_index_range_ref<handle_index_mismatch, handle_index_range_error>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS r, static_cast<const_pointer_type>(&r) - static_cast<const_pointer_type>(&a.front()), a);
}
2014-10-02 03:02:34 +00:00
template <typename managed_type>
template <typename handle_index_mismatch, typename handle_index_range_error>
void valptridx<managed_type>::check_explicit_index_range_ref(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const_reference_type &r, std::size_t i, const array_managed_type &a)
{
const auto ii = check_index_range_size<handle_index_range_error>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, &a);
check_index_match<handle_index_mismatch>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS r, ii, a);
}
template <typename managed_type>
class valptridx<managed_type>::partial_policy::require_valid
2014-09-12 03:22:34 +00:00
{
2014-12-02 04:36:19 +00:00
public:
static constexpr bool allow_nullptr = false;
[[nodiscard]]
static constexpr std::false_type check_allowed_invalid_index(index_type) { return {}; }
[[nodiscard]]
static constexpr bool check_nothrow_index(index_type i)
{
2020-12-26 21:17:29 +00:00
return std::less<std::size_t>()(static_cast<std::size_t>(i), array_size);
}
};
template <typename managed_type>
class valptridx<managed_type>::partial_policy::allow_invalid
{
public:
static constexpr bool allow_nullptr = true;
[[nodiscard]]
static constexpr bool check_allowed_invalid_index(index_type i)
2014-09-12 03:22:34 +00:00
{
return i == static_cast<index_type>(~0);
2014-09-12 03:22:34 +00:00
}
[[nodiscard]]
static constexpr bool check_nothrow_index(index_type i)
{
return check_allowed_invalid_index(i) || require_valid::check_nothrow_index(i);
}
};
2015-07-25 23:10:46 +00:00
template <typename managed_type>
constexpr bool valptridx<managed_type>::partial_policy::require_valid::allow_nullptr;
2015-07-25 23:10:46 +00:00
template <typename managed_type>
constexpr bool valptridx<managed_type>::partial_policy::allow_invalid::allow_nullptr;
2015-07-25 23:10:46 +00:00
template <typename managed_type>
template <template <typename> class policy>
class valptridx<managed_type>::partial_policy::apply_cv_policy
{
template <typename T>
using apply_cv_qualifier = typename policy<T>::type;
2014-09-12 03:22:34 +00:00
public:
using array_managed_type = apply_cv_qualifier<valptridx<managed_type>::array_managed_type>;
using pointer_type = apply_cv_qualifier<managed_type> *;
using reference_type = apply_cv_qualifier<managed_type> &;
};
template <typename managed_type>
class valptridx<managed_type>::vc :
public partial_policy::require_valid,
public partial_policy::template apply_cv_policy<std::add_const>
{
};
template <typename managed_type>
class valptridx<managed_type>::vm :
public partial_policy::require_valid,
public partial_policy::template apply_cv_policy<std::remove_const>
{
};
template <typename managed_type>
class valptridx<managed_type>::ic :
public partial_policy::allow_invalid,
public partial_policy::template apply_cv_policy<std::add_const>
{
};
template <typename managed_type>
class valptridx<managed_type>::im :
public partial_policy::allow_invalid,
public partial_policy::template apply_cv_policy<std::remove_const>
{
};
template <typename managed_type>
template <typename policy>
class valptridx<managed_type>::idx :
public policy
{
using containing_type = valptridx<managed_type>;
public:
using policy::allow_nullptr;
using policy::check_allowed_invalid_index;
using index_type = typename containing_type::index_type;
using integral_type = typename containing_type::integral_type;
using typename policy::array_managed_type;
idx() = delete;
idx(const idx &) = default;
idx(idx &&) = default;
idx &operator=(const idx &) & = default;
idx &operator=(idx &&) & = default;
idx &operator=(const idx &) && = delete;
idx &operator=(idx &&) && = delete;
index_type get_unchecked_index() const { return m_idx; }
/* If moving from allow_invalid to allow_invalid, no check is
* needed.
* If moving from require_valid to anything, no check is needed.
*/
template <typename rpolicy>
requires(policy::allow_nullptr || !rpolicy::allow_nullptr)
idx(const idx<rpolicy> &rhs) :
m_idx(rhs.get_unchecked_index())
{
}
template <typename rpolicy>
requires(!(policy::allow_nullptr || !rpolicy::allow_nullptr))
idx(const idx<rpolicy> &rhs DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) :
/* If moving from allow_invalid to require_valid, check range.
*/
m_idx(check_index_range<index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS rhs.get_unchecked_index(), nullptr))
{
}
template <typename rpolicy>
idx(idx<rpolicy> &&rhs) :
m_idx(rhs.get_unchecked_index())
2014-09-12 03:22:34 +00:00
{
/* Prevent move from allow_invalid into require_valid. The
* right hand side must be saved and checked for validity before
* being used to initialize a require_valid type.
*/
static_assert(policy::allow_nullptr || !rpolicy::allow_nullptr, "cannot move from allow_invalid to require_valid");
2014-09-12 03:22:34 +00:00
}
idx(index_type i DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) :
m_idx(check_allowed_invalid_index(i) ? i : check_index_range<index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, nullptr))
{
}
idx(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS index_type i, array_managed_type &a) :
m_idx(check_allowed_invalid_index(i) ? i : check_index_range<index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, &a))
2014-09-12 03:22:34 +00:00
{
}
protected:
template <integral_type v>
idx(const magic_constant<v> &, const allow_none_construction *) :
m_idx(v)
{
static_assert(!allow_nullptr, "allow_none_construction used where nullptr was already legal");
static_assert(static_cast<std::size_t>(v) >= array_size, "allow_none_construction used with valid index");
}
idx(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS index_type i, array_managed_type &a, const allow_end_construction *) :
m_idx(check_index_range<index_range_error_type<array_managed_type>, std::less_equal>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, &a))
{
}
idx(index_type i, array_managed_type &, const assume_nothrow_index *) :
m_idx(i)
{
}
idx(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS typename policy::pointer_type p, array_managed_type &a) :
m_idx(check_index_range_size<index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS p - &a.front(), &a))
{
}
public:
template <integral_type v>
constexpr idx(const magic_constant<v> &) :
m_idx(v)
{
static_assert(allow_nullptr || static_cast<std::size_t>(v) < array_size, "invalid magic index not allowed for this policy");
}
template <typename rpolicy>
constexpr bool operator==(const idx<rpolicy> &rhs) const
{
return m_idx == rhs.get_unchecked_index();
}
constexpr bool operator==(const index_type &i) const
2014-09-12 03:22:34 +00:00
{
return m_idx == i;
2014-09-12 03:22:34 +00:00
}
template <integral_type v>
constexpr bool operator==(const magic_constant<v> &) const
{
static_assert(allow_nullptr || static_cast<std::size_t>(v) < array_size, "invalid magic index not allowed for this policy");
return m_idx == v;
}
operator index_type() const
2014-09-12 03:22:34 +00:00
{
return m_idx;
2014-09-12 03:22:34 +00:00
}
protected:
index_type m_idx;
idx &operator++()
{
2020-12-20 20:39:07 +00:00
if constexpr (std::is_enum<index_type>::value)
m_idx = static_cast<index_type>(1u + static_cast<typename std::underlying_type<index_type>::type>(m_idx));
else
++ m_idx;
return *this;
}
idx &operator--()
{
if constexpr (std::is_enum<index_type>::value)
m_idx = static_cast<index_type>(static_cast<typename std::underlying_type<index_type>::type>(m_idx) - 1u);
else
-- m_idx;
return *this;
}
2014-09-12 03:22:34 +00:00
};
template <typename managed_type>
template <typename policy>
class valptridx<managed_type>::ptr :
public policy
Workaround compilers mishandling template-template arguments Visual Studio 2013 Update 4 and Clang 3.4 fail to parse valptridx_template_t related argument lists because they misinterpret the unspecialized inner template name as a reference to the current specialization. This seems to be nonconforming in C++11. All gcc versions supported by Rebirth parse this sample program correctly, but neither Visual Studio nor clang accept it. template <template <typename> class> struct A { }; template <typename> struct B { B(A<B>); }; In <https://stackoverflow.com/questions/17687459/clang-not-accepting-use-of-template-template-parameter-when-using-crtp>, a user saw a similar failure and received the answer: Your code is legal. From the C++11 Standard, section 14.6.1: Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-name can be used as a template-name or a type-name. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type- specifier of a friend class template declaration, it refers to the class template itself. Looks like your version of clang is still implementing the old rule. This failure was first seen with Visual Studio 2013, but ignored because Visual Studio has so many other problems parsing Rebirth. Now that it has been reported to affect clang as well, a workaround is clearly needed. A fix was suggested by btb <https://github.com/btb/dxx-rebirth/commit/2f9543f9812b584ecf1210dee0ac39f738ecd685>, but that fix breaks gcc. Rewrite the valptridx_template_t parameter passing to avoid mentioning unspecialized template names as template parameters while defining the class used as the parameter. Reported by btb (clang): https://github.com/dxx-rebirth/dxx-rebirth/pull/12
2014-11-30 23:44:52 +00:00
{
using containing_type = valptridx<managed_type>;
public:
using policy::allow_nullptr;
using policy::check_allowed_invalid_index;
2016-02-12 04:02:28 +00:00
using index_type = typename containing_type::index_type;
using const_pointer_type = typename containing_type::const_pointer_type;
using mutable_pointer_type = typename containing_type::mutable_pointer_type;
using allow_none_construction = typename containing_type::allow_none_construction;
using typename policy::array_managed_type;
using typename policy::pointer_type;
using typename policy::reference_type;
Workaround compilers mishandling template-template arguments Visual Studio 2013 Update 4 and Clang 3.4 fail to parse valptridx_template_t related argument lists because they misinterpret the unspecialized inner template name as a reference to the current specialization. This seems to be nonconforming in C++11. All gcc versions supported by Rebirth parse this sample program correctly, but neither Visual Studio nor clang accept it. template <template <typename> class> struct A { }; template <typename> struct B { B(A<B>); }; In <https://stackoverflow.com/questions/17687459/clang-not-accepting-use-of-template-template-parameter-when-using-crtp>, a user saw a similar failure and received the answer: Your code is legal. From the C++11 Standard, section 14.6.1: Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-name can be used as a template-name or a type-name. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type- specifier of a friend class template declaration, it refers to the class template itself. Looks like your version of clang is still implementing the old rule. This failure was first seen with Visual Studio 2013, but ignored because Visual Studio has so many other problems parsing Rebirth. Now that it has been reported to affect clang as well, a workaround is clearly needed. A fix was suggested by btb <https://github.com/btb/dxx-rebirth/commit/2f9543f9812b584ecf1210dee0ac39f738ecd685>, but that fix breaks gcc. Rewrite the valptridx_template_t parameter passing to avoid mentioning unspecialized template names as template parameters while defining the class used as the parameter. Reported by btb (clang): https://github.com/dxx-rebirth/dxx-rebirth/pull/12
2014-11-30 23:44:52 +00:00
ptr() = delete;
/* Override template matches to make same-type copy/move trivial */
ptr(const ptr &) = default;
ptr(ptr &&) = default;
ptr &operator=(const ptr &) & = default;
ptr &operator=(ptr &&) & = default;
ptr &operator=(const ptr &) && = delete;
ptr &operator=(ptr &&) && = delete;
Workaround compilers mishandling template-template arguments Visual Studio 2013 Update 4 and Clang 3.4 fail to parse valptridx_template_t related argument lists because they misinterpret the unspecialized inner template name as a reference to the current specialization. This seems to be nonconforming in C++11. All gcc versions supported by Rebirth parse this sample program correctly, but neither Visual Studio nor clang accept it. template <template <typename> class> struct A { }; template <typename> struct B { B(A<B>); }; In <https://stackoverflow.com/questions/17687459/clang-not-accepting-use-of-template-template-parameter-when-using-crtp>, a user saw a similar failure and received the answer: Your code is legal. From the C++11 Standard, section 14.6.1: Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-name can be used as a template-name or a type-name. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type- specifier of a friend class template declaration, it refers to the class template itself. Looks like your version of clang is still implementing the old rule. This failure was first seen with Visual Studio 2013, but ignored because Visual Studio has so many other problems parsing Rebirth. Now that it has been reported to affect clang as well, a workaround is clearly needed. A fix was suggested by btb <https://github.com/btb/dxx-rebirth/commit/2f9543f9812b584ecf1210dee0ac39f738ecd685>, but that fix breaks gcc. Rewrite the valptridx_template_t parameter passing to avoid mentioning unspecialized template names as template parameters while defining the class used as the parameter. Reported by btb (clang): https://github.com/dxx-rebirth/dxx-rebirth/pull/12
2014-11-30 23:44:52 +00:00
pointer_type get_unchecked_pointer() const { return m_ptr; }
pointer_type get_nonnull_pointer(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_DECL_VARS) const
{
/* If !allow_nullptr, assume nullptr was caught at construction. */
const auto p = m_ptr;
if constexpr (allow_nullptr)
check_null_pointer_conversion<null_pointer_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS p);
else
DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_VOID_VARS();
return p;
}
reference_type get_checked_reference(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_DECL_VARS) const
{
return *get_nonnull_pointer(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VA());
}
ptr(std::nullptr_t) :
m_ptr(nullptr)
2014-08-23 23:53:56 +00:00
{
static_assert(allow_nullptr, "nullptr construction not allowed for this policy");
2014-08-23 23:53:56 +00:00
}
2014-09-12 03:22:34 +00:00
template <integral_type v>
ptr(const magic_constant<v> &) :
2015-07-13 01:09:37 +00:00
m_ptr(nullptr)
{
static_assert(static_cast<std::size_t>(v) >= array_size, "valid magic index requires an array");
if constexpr (!allow_nullptr)
static_assert(static_cast<std::size_t>(v) < array_size, "invalid magic index not allowed for this policy");
}
2015-07-13 01:09:37 +00:00
template <integral_type v>
ptr(const magic_constant<v> &, array_managed_type &a) :
m_ptr(&a[v])
2015-07-13 01:09:37 +00:00
{
static_assert(static_cast<std::size_t>(v) < array_size, "valid magic index required when using array");
2015-07-13 01:09:37 +00:00
}
template <typename rpolicy>
requires(policy::allow_nullptr || !rpolicy::allow_nullptr)
ptr(const ptr<rpolicy> &rhs) :
m_ptr(rhs.get_unchecked_pointer())
{
}
template <typename rpolicy>
requires(!(policy::allow_nullptr || !rpolicy::allow_nullptr))
ptr(const ptr<rpolicy> &rhs DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) :
m_ptr(rhs.get_unchecked_pointer())
{
check_null_pointer_conversion<null_pointer_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS m_ptr);
}
template <typename rpolicy>
ptr(ptr<rpolicy> &&rhs) :
m_ptr(rhs.get_unchecked_pointer())
{
/* Prevent move from allow_invalid into require_valid. The
* right hand side must be saved and checked for validity before
* being used to initialize a require_valid type.
*/
static_assert(policy::allow_nullptr || !rpolicy::allow_nullptr, "cannot move from allow_invalid to require_valid");
}
ptr(index_type i) = delete;
ptr(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS index_type i, array_managed_type &a) :
m_ptr(check_allowed_invalid_index(i) ? nullptr : &a[check_index_range<index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, &a)])
{
}
ptr(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS index_type i, array_managed_type &a, const allow_end_construction *) :
m_ptr(std::next(a.begin(), static_cast<std::size_t>(check_index_range<index_range_error_type<array_managed_type>, std::less_equal>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, &a))))
{
}
ptr(index_type i, array_managed_type &a, const assume_nothrow_index *) :
m_ptr(&a[i])
{
}
ptr(pointer_type p) = delete;
ptr(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS pointer_type p, array_managed_type &a) :
/* No array consistency check here, since some code incorrectly
* defines instances of `object` outside the Objects array, then
* passes pointers to those instances to this function.
*/
m_ptr(p)
2014-09-12 03:22:34 +00:00
{
if constexpr (!allow_nullptr)
check_null_pointer<null_pointer_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS p, a);
2014-09-12 03:22:34 +00:00
}
ptr(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS reference_type r, array_managed_type &a) :
m_ptr((check_implicit_index_range_ref<index_mismatch_error_type<array_managed_type>, index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS r, a), &r))
{
}
ptr(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS reference_type r, index_type i, array_managed_type &a) :
m_ptr((check_explicit_index_range_ref<index_mismatch_error_type<array_managed_type>, index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS r, i, a), &r))
2014-09-12 03:22:34 +00:00
{
}
operator mutable_pointer_type() const { return m_ptr; } // implicit pointer conversion deprecated
operator const_pointer_type() const { return m_ptr; } // implicit pointer conversion deprecated
template <typename rpolicy>
requires(!std::is_same<policy, rpolicy>::value)
ptr rebind_policy(ptr<rpolicy> &&rhs) const
2018-09-19 02:13:30 +00:00
{
/* This method could be marked as `static`, but is non-static so
* that callers must possess an instance of the target type.
* This serves as a basic check against casts that could remove
* `const` incorrectly.
*/
return ptr(std::move(rhs), static_cast<const typename containing_type::rebind_policy *>(nullptr));
}
pointer_type operator->() const &
{
return get_nonnull_pointer();
}
operator reference_type() const &
2014-10-02 03:02:34 +00:00
{
return get_checked_reference();
2014-10-02 03:02:34 +00:00
}
reference_type operator*() const &
{
return get_checked_reference();
}
explicit operator bool() const &
2014-09-12 03:22:34 +00:00
{
return !(*this == nullptr);
2014-09-12 03:22:34 +00:00
}
pointer_type operator->() const &&
2014-11-23 04:58:45 +00:00
{
static_assert(!allow_nullptr, "operator-> not allowed with allow_invalid policy");
return operator->();
2014-11-23 04:58:45 +00:00
}
operator reference_type() const &&
2014-09-12 03:22:34 +00:00
{
static_assert(!allow_nullptr, "implicit reference not allowed with allow_invalid policy");
return *this;
2014-09-12 03:22:34 +00:00
}
reference_type operator*() const &&
2014-10-02 03:02:34 +00:00
{
static_assert(!allow_nullptr, "operator* not allowed with allow_invalid policy");
return *this;
2014-10-02 03:02:34 +00:00
}
explicit operator bool() const && = delete;
constexpr bool operator==(std::nullptr_t) const
2014-10-02 03:02:34 +00:00
{
static_assert(allow_nullptr, "nullptr comparison not allowed: value is never null");
return m_ptr == nullptr;
2014-10-02 03:02:34 +00:00
}
constexpr bool operator==(const_pointer_type p) const
2014-10-02 03:02:34 +00:00
{
return m_ptr == p;
2014-10-02 03:02:34 +00:00
}
constexpr bool operator==(mutable_pointer_type p) const
2014-10-02 03:02:34 +00:00
{
return m_ptr == p;
2014-10-02 03:02:34 +00:00
}
template <typename rpolicy>
constexpr bool operator==(const ptr<rpolicy> &rhs) const
{
return *this == rhs.get_unchecked_pointer();
}
template <typename U>
long operator-(U) const = delete;
template <typename R>
bool operator<(R) const = delete;
template <typename R>
bool operator>(R) const = delete;
template <typename R>
bool operator<=(R) const = delete;
template <typename R>
bool operator>=(R) const = delete;
protected:
pointer_type m_ptr;
ptr &operator++()
{
++ m_ptr;
return *this;
}
ptr &operator--()
{
-- m_ptr;
return *this;
}
ptr(const allow_none_construction *) :
m_ptr(nullptr)
{
static_assert(!allow_nullptr, "allow_none_construction used where nullptr was already legal");
}
template <typename rpolicy>
ptr(ptr<rpolicy> &&rhs, const typename containing_type::rebind_policy *) :
2018-09-19 02:13:30 +00:00
m_ptr(const_cast<managed_type *>(rhs.get_unchecked_pointer()))
{
static_assert(allow_nullptr || !rpolicy::allow_nullptr, "cannot rebind from allow_invalid to require_valid");
}
2014-09-12 03:22:34 +00:00
};
#if DXX_VALPTRIDX_ENFORCE_STRICT_PI_SEPARATION
template <typename T>
struct strong_typedef : T
{
using T::T;
template <typename O>
requires(std::is_constructible<T, O &&>::value)
strong_typedef(O &&o) :
T(std::forward<O>(o))
{
}
strong_typedef() = default;
strong_typedef(const strong_typedef &) = default;
strong_typedef(strong_typedef &&) = default;
strong_typedef &operator=(const strong_typedef &) & = default;
strong_typedef &operator=(strong_typedef &&) & = default;
strong_typedef &operator=(const strong_typedef &) && = delete;
strong_typedef &operator=(strong_typedef &&) && = delete;
};
#endif
template <typename managed_type>
template <typename policy>
class valptridx<managed_type>::ptridx :
public prohibit_void_ptr<ptridx<policy>>,
public ptr<policy>,
public idx<policy>
2014-09-12 03:22:34 +00:00
{
2018-09-19 02:13:30 +00:00
using containing_type = valptridx<managed_type>;
2014-09-12 03:22:34 +00:00
public:
using vptr_type = ptr<policy>;
using vidx_type = idx<policy>;
using typename vidx_type::array_managed_type;
2016-02-12 04:02:28 +00:00
using index_type = typename vidx_type::index_type;
using typename vidx_type::integral_type;
using typename vptr_type::pointer_type;
using vidx_type::operator==;
using vptr_type::operator==;
ptridx(const ptridx &) = default;
ptridx(ptridx &&) = default;
ptridx &operator=(const ptridx &) & = default;
ptridx &operator=(ptridx &&) & = default;
ptridx &operator=(const ptridx &) && = delete;
ptridx &operator=(ptridx &&) && = delete;
ptridx(std::nullptr_t) = delete;
/* Prevent implicit conversion. Require use of the factory function.
*/
ptridx(pointer_type p) = delete;
template <typename rpolicy>
requires(policy::allow_nullptr || !rpolicy::allow_nullptr)
ptridx(const ptridx<rpolicy> &rhs) :
vptr_type(static_cast<const typename ptridx<rpolicy>::vptr_type &>(rhs)),
vidx_type(static_cast<const typename ptridx<rpolicy>::vidx_type &>(rhs))
{
}
template <typename rpolicy>
requires(!(policy::allow_nullptr || !rpolicy::allow_nullptr))
ptridx(const ptridx<rpolicy> &rhs DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) :
vptr_type(static_cast<const typename ptridx<rpolicy>::vptr_type &>(rhs) DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_PASS_VARS),
vidx_type(static_cast<const typename ptridx<rpolicy>::vidx_type &>(rhs) DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_PASS_VARS)
2014-08-23 23:53:56 +00:00
{
}
template <typename rpolicy>
ptridx(ptridx<rpolicy> &&rhs) :
vptr_type(static_cast<typename ptridx<rpolicy>::vptr_type &&>(rhs)),
vidx_type(static_cast<typename ptridx<rpolicy>::vidx_type &&>(rhs))
{
}
template <integral_type v>
ptridx(const magic_constant<v> &m) :
vptr_type(m),
vidx_type(m)
{
}
template <integral_type v>
ptridx(const magic_constant<v> &m, array_managed_type &a) :
vptr_type(m, a),
vidx_type(m)
{
}
template <integral_type v>
ptridx(const magic_constant<v> &m, const allow_none_construction *const n) :
vptr_type(n),
vidx_type(m, n)
{
}
ptridx(index_type i) = delete;
ptridx(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS index_type i, array_managed_type &a) :
vptr_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, a),
vidx_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, a)
{
}
ptridx(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS index_type i, array_managed_type &a, const allow_end_construction *e) :
vptr_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, a, e),
vidx_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, a, e)
{
}
ptridx(index_type i, array_managed_type &a, const assume_nothrow_index *e) :
vptr_type(i, a, e),
vidx_type(i, a, e)
{
}
ptridx(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS pointer_type p, array_managed_type &a) :
/* Null pointer is never allowed when an index must be computed.
* Check for null, then use the reference constructor for
* vptr_type to avoid checking again.
*/
vptr_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS (check_null_pointer<null_pointer_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS p, a), *p), a),
vidx_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS p, a)
{
}
ptridx(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS pointer_type p, index_type i, array_managed_type &a) :
vptr_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS (check_null_pointer<null_pointer_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS p, a), *p), i, a),
vidx_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, a)
{
}
2018-09-19 02:13:30 +00:00
template <typename rpolicy>
requires(!std::is_same<policy, rpolicy>::value)
ptridx rebind_policy(ptridx<rpolicy> &&rhs) const
2018-09-19 02:13:30 +00:00
{
return ptridx(std::move(rhs), static_cast<const typename containing_type::rebind_policy *>(nullptr));
}
ptridx absolute_sibling(const index_type i DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) const
{
static_assert(!policy::allow_nullptr, "absolute_sibling not allowed with invalid ptridx");
ptridx r(*this);
r.m_ptr += check_index_range<index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, nullptr) - this->m_idx;
r.m_idx = i;
return r;
}
template <typename rpolicy>
constexpr bool operator==(const ptridx<rpolicy> &rhs) const
{
return vptr_type::operator==(static_cast<const typename ptridx<rpolicy>::vptr_type &>(rhs));
}
protected:
ptridx &operator++()
{
vptr_type::operator++();
vidx_type::operator++();
return *this;
}
ptridx &operator--()
{
vptr_type::operator--();
vidx_type::operator--();
return *this;
}
2018-09-19 02:13:30 +00:00
template <typename rpolicy>
ptridx(ptridx<rpolicy> &&rhs, const typename containing_type::rebind_policy *const rebind) :
vptr_type(static_cast<typename ptridx<rpolicy>::vptr_type &&>(rhs), rebind),
vidx_type(static_cast<typename ptridx<rpolicy>::vidx_type &&>(rhs))
{
/* No static_assert for policy compatibility. Incompatible
* policy conversions will be trapped by the static_assert in
* `vptr_type`.
*/
}
};
template <typename managed_type>
template <typename guarded_type>
class valptridx<managed_type>::guarded
{
static_assert(std::is_trivially_destructible<guarded_type>::value, "non-trivial destructor found for guarded_type");
enum state : uint8_t
{
/* empty - the untrusted input was invalid, so no guarded_type
* exists
*/
empty,
/* initialized - the untrusted input was valid, so a
* guarded_type type exists, but the calling code has not yet
* tested the state of this guarded<P>
*/
initialized,
/* checked - the untrusted input was valid, and the calling code
* has called operator bool()
*/
checked,
};
union {
state m_dummy;
guarded_type m_value;
};
mutable state m_state;
public:
__attribute_cold
guarded(std::nullptr_t) :
m_dummy(), m_state(empty)
{
}
guarded(guarded_type &&v) :
m_value(std::move(v)), m_state(initialized)
{
}
[[nodiscard]]
explicit operator bool() const
{
/*
* If no contained guarded_type exists, return false.
* Otherwise, record that the result has been tested and then
* return true. operator*() uses m_state to enforce that the
* result is tested.
*/
if (unlikely(m_state == empty))
return false;
m_state = checked;
return true;
}
[[nodiscard]]
guarded_type operator*() const &
{
/*
* Correct code will always execute as if this method was just
* the return statement, with none of the sanity checks. The
* checks are present to catch misuse of this type, preferably
* at compile-time, but at least at runtime.
*/
#define DXX_VALPTRIDX_GUARDED_OBJECT_NO "access to guarded object that does not exist"
#define DXX_VALPTRIDX_GUARDED_OBJECT_MAYBE "access to guarded object that may not exist"
#ifdef DXX_CONSTANT_TRUE
{
/*
* If the validity has not been verified by the caller, then
* fail. Choose an error message and function name
* based on whether the contained type provably does not
* exist. It provably does not exist if this call is on a
* path where operator bool() returned false. It
* conditionally might not exist if this call is on a path
* where operator bool() has not been called.
*/
if (DXX_CONSTANT_TRUE(m_state == empty))
DXX_ALWAYS_ERROR_FUNCTION(DXX_VALPTRIDX_GUARDED_OBJECT_NO);
/* If the contained object might not exist: */
if (DXX_CONSTANT_TRUE(m_state == initialized))
DXX_ALWAYS_ERROR_FUNCTION(DXX_VALPTRIDX_GUARDED_OBJECT_MAYBE);
}
#endif
/*
* If the compiler does not offer constant truth analysis
* (perhaps because of insufficient optimization), then emit a
* runtime check for whether the guarded_type exists.
*
* This test can throw even if the contained object is valid, if
* the caller did not first validate that the contained object
* is valid. This restriction is necessary since inputs are
* usually valid even when untested, so throwing only on state
* `empty` would allow incorrect usage to persist in the code
* until someone happened to receive an invalid input from an
* untrusted source.
*/
if (m_state != checked)
throw std::logic_error(m_state == empty ? DXX_VALPTRIDX_GUARDED_OBJECT_NO : DXX_VALPTRIDX_GUARDED_OBJECT_MAYBE);
#undef DXX_VALPTRIDX_GUARDED_OBJECT_MAYBE
#undef DXX_VALPTRIDX_GUARDED_OBJECT_NO
return m_value;
}
guarded_type operator*() const && = delete;
};
template <typename managed_type>
class valptridx<managed_type>::array_managed_type :
public array_base_count_type,
public array_base_storage_type
{
public:
/*
* Expose the union members that act as factory methods. Leave the
* `count` union member protected.
*/
#define DXX_VALPTRIDX_ACCESS_SUBTYPE_MEMBER_FACTORIES(MANAGED_TYPE, DERIVED_TYPE_PREFIX, CONTEXT, PISUFFIX, IVPREFIX, MCPREFIX) \
using array_base_count_type::IVPREFIX ## MCPREFIX ## PISUFFIX
DXX_VALPTRIDX_FOR_EACH_PPI_TYPE(DXX_VALPTRIDX_ACCESS_SUBTYPE_MEMBER_FACTORIES,,,);
#undef DXX_VALPTRIDX_ACCESS_SUBTYPE_MEMBER_FACTORIES
using typename array_base_storage_type::reference;
using typename array_base_storage_type::const_reference;
reference operator[](const integral_type &n)
{
return array_base_storage_type::operator[](n);
}
const_reference operator[](const integral_type &n) const
{
return array_base_storage_type::operator[](n);
}
template <typename T>
reference operator[](const T &) const = delete;
#if DXX_HAVE_POISON_UNDEFINED
array_managed_type();
#else
array_managed_type() = default;
#endif
array_managed_type(const array_managed_type &) = delete;
array_managed_type &operator=(const array_managed_type &) = delete;
};
template <typename managed_type>
template <typename Pc, typename Pm>
class valptridx<managed_type>::basic_ival_member_factory
{
using containing_type = valptridx<managed_type>;
protected:
/*
* These casts are well-defined:
* - The reinterpret_cast is defined because
* `basic_ival_member_factory` is a base of a member of the
* anonymous union in `array_base_count_type`.
* - The anonymous union in `array_base_count_type` is the only
* member of that type, so its storage must be aligned to the
* beginning of the object.
* - `basic_ival_member_factory` and its derivatives are not used
* anywhere other than `array_base_count_type`, so any call to
* these methods must be on an instance used in
* `array_base_count_type`.
*
* - The static_cast is defined because `array_base_count_type` is a
* non-virtual base of `array_managed_type`.
* - `array_base_count_type` is not used as a base for any class
* other than `array_managed_type`, nor used freestanding, so any
* instance of `array_base_count_type` must be safe to downcast to
* `array_managed_type`.
*/
constexpr const array_managed_type &get_array() const
{
return static_cast<const array_managed_type &>(reinterpret_cast<const array_base_count_type &>(*this));
}
array_managed_type &get_array()
2016-12-10 17:51:07 +00:00
{
return static_cast<array_managed_type &>(reinterpret_cast<array_base_count_type &>(*this));
2016-12-10 17:51:07 +00:00
}
template <typename P, typename A>
static guarded<P> check_untrusted_internal(const index_type i, A &a)
{
if (P::check_nothrow_index(i))
return P(i, a, static_cast<const assume_nothrow_index *>(nullptr));
else
return nullptr;
}
template <typename P, typename T, typename A>
static guarded<P> check_untrusted_internal(T &&, A &) = delete;
template <typename P, typename A>
[[nodiscard]]
/* C++ does not allow `static operator()()`, so name it
* `call_operator` instead.
*/
static P call_operator(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const typename P::index_type i, A &a)
2016-12-10 17:51:07 +00:00
{
return P(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, a);
2016-12-10 17:51:07 +00:00
}
template <typename P, containing_type::integral_type v, typename A>
[[nodiscard]]
static P call_operator(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const containing_type::magic_constant<v> &m, A &a)
{
/*
* All call_operator definitions must have the macro which
* defines filename/lineno, but the magic_constant overload
* has no need for them.
*
* Cast them to void to silence the warning about unused
* parameters.
*/
DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_VOID_VARS();
return P(m, a);
}
template <typename P, typename T, typename A>
static P call_operator(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS T &&, A &a) = delete;
basic_ival_member_factory() = default;
public:
basic_ival_member_factory(const basic_ival_member_factory &) = delete;
basic_ival_member_factory &operator=(const basic_ival_member_factory &) = delete;
2016-12-10 17:51:07 +00:00
void *operator &() const = delete;
template <typename T>
[[nodiscard]]
guarded<Pc> check_untrusted(T &&t) const
{
return this->template check_untrusted_internal<Pc>(static_cast<T &&>(t), get_array());
}
template <typename T>
[[nodiscard]]
guarded<Pm> check_untrusted(T &&t)
{
return this->template check_untrusted_internal<Pm>(static_cast<T &&>(t), get_array());
}
template <typename T>
[[nodiscard]]
Pc operator()(T &&t DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) const
{
return this->template call_operator<Pc>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS static_cast<T &&>(t), get_array());
}
template <typename T>
[[nodiscard]]
Pm operator()(T &&t DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS)
{
return this->template call_operator<Pm>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS static_cast<T &&>(t), get_array());
}
2016-12-10 17:51:07 +00:00
};
template <typename managed_type>
template <typename Pc, typename Pm>
class valptridx<managed_type>::basic_vval_member_factory :
public basic_ival_member_factory<Pc, Pm>
2016-12-10 17:51:07 +00:00
{
protected:
using basic_ival_member_factory<Pc, Pm>::get_array;
using basic_ival_member_factory<Pc, Pm>::call_operator;
template <typename P>
using iterator = self_return_iterator<P>;
template <typename P, typename policy, typename A>
static P call_operator(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const typename valptridx<managed_type>::template wrapper<valptridx<managed_type>::idx<policy>> i, A &a)
{
return P(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, a);
}
template <typename P>
[[nodiscard]]
static P call_operator(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const typename P::mutable_pointer_type p, typename P::array_managed_type &a)
{
return P(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS p, a);
}
/*
* When P is a const type, require a const array, so that attempts to
* provide a mutable array fail. This provides a slightly clearer error
* when trying to pass a const pointer to a mutable factory.
*/
template <typename P>
requires(std::is_same<const array_managed_type, typename P::array_managed_type>::value)
[[nodiscard]]
static P call_operator(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const typename P::const_pointer_type p, const array_managed_type &a)
{
return P(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS p, a);
}
template <typename P, typename A>
static iterator<P> end_internal(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS A &a)
{
return P(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS static_cast<index_type>(a.get_count()), a, static_cast<const allow_end_construction *>(nullptr));
}
public:
[[nodiscard]]
typename array_base_storage_type::size_type count() const
2015-05-09 17:38:58 +00:00
{
return get_array().get_count();
2015-05-09 17:38:58 +00:00
}
[[nodiscard]]
typename array_base_storage_type::size_type size() const
2014-11-25 04:02:01 +00:00
{
return get_array().size();
2014-11-25 04:02:01 +00:00
}
template <typename T>
[[nodiscard]]
Pc operator()(T &&t DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) const
{
return this->template call_operator<Pc>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS static_cast<T &&>(t), get_array());
}
template <typename T>
[[nodiscard]]
Pm operator()(T &&t DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS)
{
return this->template call_operator<Pm>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS static_cast<T &&>(t), get_array());
}
[[nodiscard]]
iterator<Pc> begin() const
{
2020-12-20 20:39:07 +00:00
return Pc(valptridx<managed_type>::magic_constant<index_type{}>(), get_array());
}
[[nodiscard]]
iterator<Pm> begin()
{
2020-12-20 20:39:07 +00:00
return Pm(valptridx<managed_type>::magic_constant<index_type{}>(), get_array());
}
[[nodiscard]]
iterator<Pc> end(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_DECL_VARS) const
{
return this->template end_internal<Pc>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS get_array());
}
[[nodiscard]]
iterator<Pm> end(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_DECL_VARS)
{
return this->template end_internal<Pm>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS get_array());
}
2014-11-25 04:02:01 +00:00
};
#define DXX_VALPTRIDX_DEFINE_FACTORY(MANAGED_TYPE, GLOBAL_FACTORY, GLOBAL_ARRAY, MEMBER_FACTORY) \
__attribute_unused static auto &GLOBAL_FACTORY = GLOBAL_ARRAY.MEMBER_FACTORY
#define DXX_VALPTRIDX_DEFINE_GLOBAL_FACTORY(MANAGED_TYPE, DERIVED_TYPE_PREFIX, GLOBAL_ARRAY, PISUFFIX, IVPREFIX, MCPREFIX) \
DXX_VALPTRIDX_DEFINE_FACTORY(MANAGED_TYPE, IVPREFIX ## MCPREFIX ## DERIVED_TYPE_PREFIX ## PISUFFIX, GLOBAL_ARRAY, IVPREFIX ## MCPREFIX ## PISUFFIX)
#define DXX_VALPTRIDX_DEFINE_GLOBAL_FACTORIES(MANAGED_TYPE,DERIVED_TYPE_PREFIX,GLOBAL_ARRAY) \
extern MANAGED_TYPE ## _array GLOBAL_ARRAY; \
namespace { namespace { \
DXX_VALPTRIDX_FOR_EACH_PPI_TYPE(DXX_VALPTRIDX_DEFINE_GLOBAL_FACTORY, MANAGED_TYPE, DERIVED_TYPE_PREFIX, GLOBAL_ARRAY); \
} }