libcruft-util/strongdef.hpp
Danny Robson 279af4c796 strongdef: significantly tighten restrictions on usage
It turns out that equality in particular was triggering implicit
construction of strongdef types. We make it much harder for these types
to spontaneously emerge.
2018-06-22 17:41:56 +10:00

176 lines
7.3 KiB
C++

/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2015-2018 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_STRONGDEF_HPP
#define __UTIL_STRONGDEF_HPP
#include "cast.hpp"
#include "types/traits.hpp"
#include <limits>
#include <type_traits>
#include <iosfwd>
namespace util {
/// A transparent wrapper around a (typically lightweight) type for the
/// purposes of overload disambiguation. It acts like a typesafe typedef.
template <typename T,typename Tag>
struct strongdef {
public:
using value_type = T;
using tag_type = Tag;
// TODO: ideally we'd default the constructor, but templated
// constructors can't be defaulted? So we just stub one out.
template <typename = std::enable_if_t<std::is_default_constructible_v<T>>>
constexpr explicit strongdef () { ; }
constexpr explicit strongdef (util::types::identity_t<T> const &_data): data (_data) { ; }
constexpr strongdef (strongdef const&) = default;
strongdef& operator= (T const &) = delete;
strongdef& operator= (strongdef const &) = default;
// conversion operators must not be explicit or it defeats the point
// of this class (ease of use, transparency).
explicit operator const T& (void) const& { return data; }
explicit operator T& (void) & { return data; }
constexpr auto operator== (T const &) = delete;
constexpr auto operator== (strongdef const &rhs) const
{
return data == rhs.data;
}
constexpr auto operator!= (T const&) = delete;
constexpr auto operator!= (strongdef const &rhs) const
{
return data != rhs.data;
}
constexpr auto operator< (T const &) const = delete;
constexpr auto operator< (strongdef const &rhs) const { return data < rhs.data; }
constexpr auto operator> (T const &) const = delete;
constexpr auto operator> (strongdef const &rhs) const { return data > rhs.data; }
T data;
};
template <typename ValueT, typename TagT>
std::ostream&
operator<< (std::ostream &os, strongdef<ValueT,TagT> const &val)
{
return os << val.data;
}
}
template <typename ContainerT>
static auto indices (ContainerT const &obj)
{
using index_t = typename ContainerT::index_t;
struct view {
view (ContainerT const &_obj):
m_obj (_obj)
{ ; }
struct iterator {
iterator (index_t _idx):
idx (_idx)
{ ; }
iterator& operator++ ()
{
++idx.data;
return *this;
}
index_t const& operator* () const { return idx; }
index_t const* operator-> () const { return &idx; }
bool operator!= (iterator const &rhs) { return idx != rhs.idx; }
private:
index_t idx;
};
iterator begin (void) const { return iterator (index_t (0)); }
iterator end (void) const
{
return index_t (
util::cast::narrow<
typename index_t::value_type
> (
m_obj.size ()
)
);
}
private:
ContainerT const &m_obj;
};
return view {obj};
}
namespace std {
template <typename T, typename Tag>
struct numeric_limits<util::strongdef<T,Tag>> {
using value_type = typename util::strongdef<T,Tag>::value_type;
static constexpr bool is_specialized = numeric_limits<value_type>::is_specialized;
static constexpr bool is_signed = numeric_limits<value_type>::is_signed;
static constexpr bool is_integer = numeric_limits<value_type>::is_integer;
static constexpr bool is_exact = numeric_limits<value_type>::is_exact;
static constexpr bool has_infinity = numeric_limits<value_type>::has_infinity;
static constexpr bool has_quiet_NaN = numeric_limits<value_type>::has_quiet_NaN;
static constexpr bool has_signaling_NaN = numeric_limits<value_type>::has_signaling_NaN;
static constexpr bool has_denorm = numeric_limits<value_type>::has_denorm;
static constexpr bool has_denorm_loss = numeric_limits<value_type>::has_denorm_loss;
static constexpr std::float_round_style round_style = numeric_limits<value_type>::round_style;
static constexpr bool is_iec559 = numeric_limits<value_type>::is_iec559;
static constexpr bool is_bounded = numeric_limits<value_type>::is_bounded;
static constexpr bool is_modulo = numeric_limits<value_type>::is_modulo;
static constexpr int digits = numeric_limits<value_type>::digits;
static constexpr int digits10 = numeric_limits<value_type>::digits10;
static constexpr int max_digits10 = numeric_limits<value_type>::max_digits10;
static constexpr int radix = numeric_limits<value_type>::radix;
static constexpr int min_exponent = numeric_limits<value_type>::min_exponent;
static constexpr int min_exponent10 = numeric_limits<value_type>::min_exponent10;
static constexpr int max_exponent = numeric_limits<value_type>::max_exponent;
static constexpr int max_exponent10 = numeric_limits<value_type>::max_exponent10;
static constexpr bool traps = numeric_limits<value_type>::traps;
static constexpr bool tinyness_before = numeric_limits<value_type>::tinyness_before;
static constexpr value_type min (void) { return numeric_limits<value_type>::min (); }
static constexpr value_type lowest (void) { return numeric_limits<value_type>::lowest (); }
static constexpr value_type max (void) { return numeric_limits<value_type>::max (); }
static constexpr value_type epsilon (void) { return numeric_limits<value_type>::epsilon (); }
static constexpr value_type round_error (void) { return numeric_limits<value_type>::round_error (); }
static constexpr value_type infinity (void) { return numeric_limits<value_type>::infinity (); }
static constexpr value_type quiet_NaN (void) { return numeric_limits<value_type>::quiet_NaN (); }
static constexpr value_type signaling_NaN (void) { return numeric_limits<value_type>::signaling_NaN (); }
static constexpr value_type denorm_min (void) { return numeric_limits<value_type>::denorm_min (); }
};
}
#endif