forked from mpusz/mp-units
Support for derived dimensions in exp added
This commit is contained in:
@@ -74,8 +74,8 @@ constexpr units::Velocity auto avg_speed(units::Length auto d, units::Time auto
|
||||
|
||||
### `Dimensions`
|
||||
|
||||
`units::dimension` is a type-list like type that stores an ordered list of exponents of one
|
||||
or more base dimensions:
|
||||
`units::dimension` represents a derived dimension and is implemented as a type-list like type that
|
||||
stores an ordered list of exponents of one or more base dimensions:
|
||||
|
||||
```cpp
|
||||
template<Exponent... Es>
|
||||
@@ -94,28 +94,6 @@ concept Dimension =
|
||||
|
||||
#### `Exponents`
|
||||
|
||||
`units::exp` provides an information about a single base dimension and its (possibly fractional)
|
||||
exponent in a derived dimension:
|
||||
|
||||
```cpp
|
||||
template<const base_dimension& BaseDimension, int Num, int Den = 1>
|
||||
struct exp {
|
||||
static constexpr const base_dimension& dimension = BaseDimension;
|
||||
static constexpr int num = Num;
|
||||
static constexpr int den = Den;
|
||||
};
|
||||
```
|
||||
|
||||
where `BaseDimension` is a unique sortable compile-time value:
|
||||
|
||||
```cpp
|
||||
struct base_dimension {
|
||||
const char* name;
|
||||
};
|
||||
constexpr bool operator==(const base_dimension& lhs, const base_dimension& rhs);
|
||||
constexpr bool operator<(const base_dimension& lhs, const base_dimension& rhs);
|
||||
```
|
||||
|
||||
`units::Exponent` concept is satisfied if provided type is an instantiation of `units::exp` class
|
||||
template:
|
||||
|
||||
@@ -125,6 +103,45 @@ concept Exponent =
|
||||
detail::is_exp<T>; // exposition only
|
||||
```
|
||||
|
||||
`units::exp` provides an information about a single dimension and its (possibly fractional)
|
||||
exponent in a derived dimension.
|
||||
|
||||
```cpp
|
||||
template<typename Dim, int Num, int Den = 1>
|
||||
requires BaseDimension<Dim> || Dimension<Dim>
|
||||
struct exp {
|
||||
using dimension = Dim;
|
||||
static constexpr int num = Num;
|
||||
static constexpr int den = Den;
|
||||
};
|
||||
```
|
||||
|
||||
Both a base dimension and a derived dimension can be provided to `units::exp` class template.
|
||||
|
||||
`units::BaseDimension` represents a base dimension and should be implemented as a type with
|
||||
a unique compile-time text describing the dimension name:
|
||||
|
||||
```cpp
|
||||
template<typename T>
|
||||
concept BaseDimension = std::is_empty_v<T> &&
|
||||
requires {
|
||||
{ T::value } -> std::same_as<const char*>;
|
||||
};
|
||||
```
|
||||
|
||||
For example here is a list of SI base dimensions:
|
||||
|
||||
```cpp
|
||||
struct base_dim_length { static constexpr const char* value = "length"; };
|
||||
struct base_dim_mass { static constexpr const char* value = "mass"; };
|
||||
struct base_dim_time { static constexpr const char* value = "time"; };
|
||||
struct base_dim_current { static constexpr const char* value = "current"; };
|
||||
struct base_dim_temperature { static constexpr const char* value = "temperature"; };
|
||||
struct base_dim_substance { static constexpr const char* value = "substance"; };
|
||||
struct base_dim_luminous_intensity { static constexpr const char* value = "luminous intensity"; };
|
||||
```
|
||||
|
||||
|
||||
#### `make_dimension`
|
||||
|
||||
Above design of dimensions is created with the ease of use for end users in mind. Compile-time
|
||||
@@ -169,6 +186,23 @@ contained base dimensions. Beside providing ordering to base dimensions it also
|
||||
- aggregate two arguments of the same base dimension but different exponents
|
||||
- eliminate two arguments of the same base dimension and with opposite equal exponents
|
||||
|
||||
`make_dimension_t` is also able to form a dimension type based not only on base dimensions but
|
||||
it can take other derived dimensions as well. So for some more complex dimensions user can
|
||||
type either:
|
||||
|
||||
```cpp
|
||||
struct pressure : make_dimension_t<exp<base_dim_mass, 1>, exp<base_dim_length, -1>, exp<base_dim_time, -2>> {};
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```cpp
|
||||
struct pressure : make_dimension_t<exp<force, 1>, exp<area, -1>> {};
|
||||
```
|
||||
|
||||
In the second case `make_dimension_t` will extract all derived dimensions into the list of
|
||||
exponents of base dimensions. Thanks to that both cases will result with exactly the same base
|
||||
class formed only from the exponents of base units.
|
||||
|
||||
#### `merge_dimension`
|
||||
|
||||
@@ -426,7 +460,7 @@ struct downcast_base {
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
concept bool Downcastable =
|
||||
concept Downcastable =
|
||||
requires {
|
||||
typename T::base_type;
|
||||
} &&
|
||||
|
@@ -29,24 +29,19 @@
|
||||
|
||||
namespace std::experimental::units {
|
||||
|
||||
struct base_dimension {
|
||||
const char* name;
|
||||
template<typename T>
|
||||
concept bool BaseDimension = std::is_empty_v<T> &&
|
||||
requires {
|
||||
{ T::value } -> std::same_as<const char*>;
|
||||
};
|
||||
|
||||
constexpr bool operator==(const base_dimension& lhs, const base_dimension& rhs)
|
||||
{
|
||||
const char* p1 = lhs.name;
|
||||
const char* p2 = rhs.name;
|
||||
for(; (*p1 != '\0') && (*p2 != '\0'); ++p1, (void)++p2) {
|
||||
if(*p1 != *p2) return false;
|
||||
}
|
||||
return *p1 == *p2;
|
||||
}
|
||||
namespace detail {
|
||||
|
||||
constexpr bool operator<(const base_dimension& lhs, const base_dimension& rhs)
|
||||
template<BaseDimension D1, BaseDimension D2>
|
||||
constexpr bool less()
|
||||
{
|
||||
const char* p1 = lhs.name;
|
||||
const char* p2 = rhs.name;
|
||||
const char* p1 = D1::value;
|
||||
const char* p2 = D2::value;
|
||||
for(; (*p1 != '\0') && (*p2 != '\0'); ++p1, (void) ++p2) {
|
||||
if(*p1 < *p2) return true;
|
||||
if(*p2 < *p1) return false;
|
||||
@@ -54,56 +49,29 @@ namespace std::experimental::units {
|
||||
return (*p1 == '\0') && (*p2 != '\0');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// base_dimension_less
|
||||
|
||||
template<const base_dimension& D1, const base_dimension& D2>
|
||||
struct base_dimension_less : std::bool_constant<D1 < D2> {
|
||||
};
|
||||
|
||||
// exp
|
||||
|
||||
template<const base_dimension& BaseDimension, int Num, int Den = 1>
|
||||
struct exp {
|
||||
static constexpr const base_dimension& dimension = BaseDimension;
|
||||
static constexpr int num = Num;
|
||||
static constexpr int den = Den;
|
||||
template<BaseDimension D1, BaseDimension D2>
|
||||
struct base_dimension_less : std::bool_constant<detail::less<D1, D2>()> {
|
||||
};
|
||||
|
||||
// is_exp
|
||||
namespace detail {
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_exp = false;
|
||||
|
||||
template<const base_dimension& BaseDimension, int Num, int Den>
|
||||
inline constexpr bool is_exp<exp<BaseDimension, Num, Den>> = true;
|
||||
// partial specialization for an exp type provided below
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T>
|
||||
concept bool Exponent = detail::is_exp<T>;
|
||||
|
||||
// exp_dim_id_less
|
||||
|
||||
template<Exponent E1, Exponent E2>
|
||||
struct exp_less : base_dimension_less<E1::dimension, E2::dimension> {
|
||||
};
|
||||
|
||||
// exp_invert
|
||||
|
||||
template<Exponent E>
|
||||
struct exp_invert;
|
||||
|
||||
template<const base_dimension& BaseDimension, int Num, int Den>
|
||||
struct exp_invert<exp<BaseDimension, Num, Den>> {
|
||||
using type = exp<BaseDimension, -Num, Den>;
|
||||
};
|
||||
|
||||
template<Exponent E>
|
||||
using exp_invert_t = exp_invert<E>::type;
|
||||
|
||||
// dimension
|
||||
|
||||
template<Exponent... Es>
|
||||
struct dimension : downcast_base<dimension<Es...>> {};
|
||||
struct dimension;
|
||||
|
||||
// is_dimension
|
||||
namespace detail {
|
||||
@@ -121,6 +89,57 @@ namespace std::experimental::units {
|
||||
std::is_empty_v<T> &&
|
||||
detail::is_dimension<downcast_from<T>>;
|
||||
|
||||
// exp
|
||||
|
||||
template<typename Dim, int Num, int Den = 1>
|
||||
requires BaseDimension<Dim> || Dimension<Dim>
|
||||
struct exp {
|
||||
using dimension = Dim;
|
||||
static constexpr int num = Num;
|
||||
static constexpr int den = Den;
|
||||
};
|
||||
|
||||
// is_exp
|
||||
namespace detail {
|
||||
|
||||
template<typename Dim, int Num, int Den>
|
||||
inline constexpr bool is_exp<exp<Dim, Num, Den>> = true;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// exp_less
|
||||
|
||||
template<Exponent E1, Exponent E2>
|
||||
struct exp_less : base_dimension_less<typename E1::dimension, typename E2::dimension> {
|
||||
};
|
||||
|
||||
// exp_invert
|
||||
|
||||
template<Exponent E>
|
||||
struct exp_invert;
|
||||
|
||||
template<typename Dim, int Num, int Den>
|
||||
struct exp_invert<exp<Dim, Num, Den>> {
|
||||
using type = exp<Dim, -Num, Den>;
|
||||
};
|
||||
|
||||
template<Exponent E>
|
||||
using exp_invert_t = exp_invert<E>::type;
|
||||
|
||||
// exp_multiply
|
||||
|
||||
template<Exponent E, int Num, int Den>
|
||||
struct exp_multiply {
|
||||
using type = exp<typename E::dimension, E::num * Num, E::den * Den>;
|
||||
};
|
||||
|
||||
template<Exponent E, int Num, int Den>
|
||||
using exp_multiply_t = exp_multiply<E, Num, Den>::type;
|
||||
|
||||
// dimension
|
||||
|
||||
template<Exponent... Es>
|
||||
struct dimension : downcast_base<dimension<Es...>> {};
|
||||
|
||||
// dim_invert
|
||||
|
||||
@@ -161,7 +180,7 @@ namespace std::experimental::units {
|
||||
using type = conditional<std::is_same_v<rest, dimension<>>, dimension<E1>, type_list_push_front<rest, E1>>;
|
||||
};
|
||||
|
||||
template<const base_dimension& D, int Num1, int Den1, int Num2, int Den2, typename... ERest>
|
||||
template<BaseDimension D, int Num1, int Den1, int Num2, int Den2, typename... ERest>
|
||||
struct dim_consolidate<dimension<exp<D, Num1, Den1>, exp<D, Num2, Den2>, ERest...>> {
|
||||
// todo: provide custom implementation for ratio_add
|
||||
using r1 = std::ratio<Num1, Den1>;
|
||||
@@ -171,11 +190,37 @@ namespace std::experimental::units {
|
||||
dim_consolidate_t<dimension<exp<D, r::num, r::den>, ERest...>>>;
|
||||
};
|
||||
|
||||
template<Exponent... Es>
|
||||
struct extract;
|
||||
|
||||
template<Exponent... Es>
|
||||
using extract_t = extract<Es...>::type;
|
||||
|
||||
template<>
|
||||
struct extract<> {
|
||||
using type = dimension<>;
|
||||
};
|
||||
|
||||
template<BaseDimension Dim, int Num, int Den, Exponent... ERest>
|
||||
struct extract<exp<Dim, Num, Den>, ERest...> {
|
||||
using type = type_list_push_front<extract_t<ERest...>, exp<Dim, Num, Den>>;
|
||||
};
|
||||
|
||||
template<Exponent... Es, int Num, int Den, Exponent... ERest>
|
||||
struct extract<exp<dimension<Es...>, Num, Den>, ERest...> {
|
||||
using type = type_list_push_front<extract_t<ERest...>, exp_multiply_t<Es, Num, Den>...>;
|
||||
};
|
||||
|
||||
template<Dimension Dim, int Num, int Den, Exponent... ERest>
|
||||
struct extract<exp<Dim, Num, Den>, ERest...> {
|
||||
using type = extract_t<exp<downcast_from<Dim>, Num, Den>, ERest...>;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<Exponent... Es>
|
||||
struct make_dimension {
|
||||
using type = detail::dim_consolidate_t<type_list_sort<dimension<Es...>, exp_less>>;
|
||||
using type = detail::dim_consolidate_t<type_list_sort<detail::extract_t<Es...>, exp_less>>;
|
||||
};
|
||||
|
||||
template<Exponent... Es>
|
||||
|
@@ -26,7 +26,7 @@
|
||||
|
||||
namespace std::experimental::units {
|
||||
|
||||
struct acceleration : make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -2>> {};
|
||||
struct acceleration : make_dimension_t<exp<velocity, 1>, exp<base_dim_time, -1>> {};
|
||||
template<> struct downcasting_traits<downcast_from<acceleration>> : downcast_to<acceleration> {};
|
||||
|
||||
template<typename T>
|
||||
|
@@ -26,12 +26,12 @@
|
||||
|
||||
namespace std::experimental::units {
|
||||
|
||||
inline constexpr base_dimension base_dim_length{"length"};
|
||||
inline constexpr base_dimension base_dim_mass{"mass"};
|
||||
inline constexpr base_dimension base_dim_time{"time"};
|
||||
inline constexpr base_dimension base_dim_current{"current"};
|
||||
inline constexpr base_dimension base_dim_temperature{"temperature"};
|
||||
inline constexpr base_dimension base_dim_substance{"substance"};
|
||||
inline constexpr base_dimension base_dim_luminous_intensity{"luminous intensity"};
|
||||
struct base_dim_length { static constexpr const char* value = "length"; };
|
||||
struct base_dim_mass { static constexpr const char* value = "mass"; };
|
||||
struct base_dim_time { static constexpr const char* value = "time"; };
|
||||
struct base_dim_current { static constexpr const char* value = "current"; };
|
||||
struct base_dim_temperature { static constexpr const char* value = "temperature"; };
|
||||
struct base_dim_substance { static constexpr const char* value = "substance"; };
|
||||
struct base_dim_luminous_intensity { static constexpr const char* value = "luminous intensity"; };
|
||||
|
||||
} // namespace std::experimental::units
|
||||
|
@@ -28,7 +28,7 @@
|
||||
|
||||
namespace std::experimental::units {
|
||||
|
||||
struct capacitance : make_dimension_t<exp<base_dim_mass, -1>, exp<base_dim_length, -2>, exp<base_dim_time, 4>, exp<base_dim_current, 2>> {};
|
||||
struct capacitance : make_dimension_t<exp<electric_charge, 1>, exp<voltage, -1>> {};
|
||||
template<> struct downcasting_traits<downcast_from<capacitance>> : downcast_to<capacitance> {};
|
||||
|
||||
template<typename T>
|
||||
|
@@ -28,7 +28,7 @@
|
||||
|
||||
namespace std::experimental::units {
|
||||
|
||||
struct energy : make_dimension_t<exp<base_dim_mass, 1>, exp<base_dim_length, 2>, exp<base_dim_time, -2>> {};
|
||||
struct energy : make_dimension_t<exp<force, 1>, exp<length, 1>> {};
|
||||
template<> struct downcasting_traits<downcast_from<energy>> : downcast_to<energy> {};
|
||||
|
||||
template<typename T>
|
||||
|
@@ -24,12 +24,11 @@
|
||||
|
||||
#include <units/dimensions/base_dimensions.h>
|
||||
#include <units/dimensions/mass.h>
|
||||
#include <units/dimensions/length.h>
|
||||
#include <units/dimensions/time.h>
|
||||
#include <units/dimensions/acceleration.h>
|
||||
|
||||
namespace std::experimental::units {
|
||||
|
||||
struct force : make_dimension_t<exp<base_dim_mass, 1>, exp<base_dim_length, 1>, exp<base_dim_time, -2>> {};
|
||||
struct force : make_dimension_t<exp<base_dim_mass, 1>, exp<acceleration, 1>> {};
|
||||
template<> struct downcasting_traits<downcast_from<force>> : downcast_to<force> {};
|
||||
|
||||
template<typename T>
|
||||
|
@@ -27,7 +27,7 @@
|
||||
|
||||
namespace std::experimental::units {
|
||||
|
||||
struct power : make_dimension_t<exp<base_dim_mass, 1>, exp<base_dim_length, 2>, exp<base_dim_time, -3>> {};
|
||||
struct power : make_dimension_t<exp<energy, 1>, exp<base_dim_time, -1>> {};
|
||||
template<> struct downcasting_traits<downcast_from<power>> : downcast_to<power> {};
|
||||
|
||||
template<typename T>
|
||||
|
@@ -24,10 +24,11 @@
|
||||
|
||||
#include <units/dimensions/base_dimensions.h>
|
||||
#include <units/dimensions/force.h>
|
||||
#include <units/dimensions/area.h>
|
||||
|
||||
namespace std::experimental::units {
|
||||
|
||||
struct pressure : make_dimension_t<exp<base_dim_mass, 1>, exp<base_dim_length, -1>, exp<base_dim_time, -2>> {};
|
||||
struct pressure : make_dimension_t<exp<force, 1>, exp<area, -1>> {};
|
||||
template<> struct downcasting_traits<downcast_from<pressure>> : downcast_to<pressure> {};
|
||||
|
||||
template<typename T>
|
||||
|
@@ -30,7 +30,7 @@
|
||||
|
||||
namespace std::experimental::units {
|
||||
|
||||
struct voltage : make_dimension_t<exp<base_dim_mass, 1>, exp<base_dim_length, 2>, exp<base_dim_time, -3>, exp<base_dim_current, -1>> {};
|
||||
struct voltage : make_dimension_t<exp<power, 1>, exp<base_dim_current, -1>> {};
|
||||
template<> struct downcasting_traits<downcast_from<voltage>> : downcast_to<voltage> {};
|
||||
|
||||
template<typename T>
|
||||
|
@@ -63,19 +63,19 @@ namespace std::experimental::units {
|
||||
template<typename E, typename... Rest>
|
||||
struct get_unit_base_dim<dimension<E, Rest...>> {
|
||||
static_assert(sizeof...(Rest) == 0, "Base unit expected");
|
||||
static constexpr const base_dimension& dimension = E::dimension;
|
||||
using dimension = E::dimension;
|
||||
};
|
||||
|
||||
template<const base_dimension& BaseDimension, typename... Us>
|
||||
template<BaseDimension BD, typename... Us>
|
||||
struct get_ratio {
|
||||
using ratio = ::std::experimental::units::ratio<1>;
|
||||
};
|
||||
|
||||
template<const base_dimension& BaseDimension, typename U, typename... Rest>
|
||||
struct get_ratio<BaseDimension, U, Rest...> {
|
||||
static constexpr const base_dimension& unit_base_dim = get_unit_base_dim<typename U::dimension::base_type>::dimension;
|
||||
using ratio = conditional<&unit_base_dim == &BaseDimension, typename U::ratio,
|
||||
typename get_ratio<BaseDimension, Rest...>::ratio>;
|
||||
template<BaseDimension BD, typename U, typename... Rest>
|
||||
struct get_ratio<BD, U, Rest...> {
|
||||
using unit_base_dim = get_unit_base_dim<typename U::dimension::base_type>::dimension;
|
||||
using ratio = conditional<std::is_same_v<unit_base_dim, BD>, typename U::ratio,
|
||||
typename get_ratio<BD, Rest...>::ratio>;
|
||||
};
|
||||
|
||||
template<typename Result, int UnitExpNum, int UnitExpDen, typename UnitRatio>
|
||||
@@ -105,7 +105,7 @@ namespace std::experimental::units {
|
||||
template<typename E, typename... Rest, typename... Us>
|
||||
struct derived_ratio<dimension<E, Rest...>, Us...> {
|
||||
using rest_ratio = derived_ratio<dimension<Rest...>, Us...>::ratio;
|
||||
using e_ratio = get_ratio<E::dimension, Us...>::ratio;
|
||||
using e_ratio = get_ratio<typename E::dimension, Us...>::ratio;
|
||||
using ratio = ratio_op<rest_ratio, E::num, E::den, e_ratio>::ratio;
|
||||
};
|
||||
|
||||
|
@@ -29,7 +29,7 @@ namespace {
|
||||
|
||||
using namespace std::experimental;
|
||||
|
||||
inline constexpr units::base_dimension base_dim_digital_information{"digital information"};
|
||||
struct base_dim_digital_information { static constexpr const char* value = "digital information"; };
|
||||
|
||||
struct digital_information : units::make_dimension_t<units::exp<base_dim_digital_information, 1>> {};
|
||||
|
||||
|
@@ -27,16 +27,31 @@ using namespace std::experimental::units;
|
||||
|
||||
namespace {
|
||||
|
||||
inline constexpr base_dimension d0{"d0"};
|
||||
inline constexpr base_dimension d1{"d1"};
|
||||
inline constexpr base_dimension d2{"d2"};
|
||||
inline constexpr base_dimension d3{"d3"};
|
||||
struct d0 { static constexpr const char* value = "d0"; };
|
||||
struct d1 { static constexpr const char* value = "d1"; };
|
||||
struct d2 { static constexpr const char* value = "d2"; };
|
||||
struct d3 { static constexpr const char* value = "d3"; };
|
||||
|
||||
// exp_invert
|
||||
|
||||
static_assert(std::is_same_v<exp_invert_t<exp<d0, 1>>, exp<d0, -1>>);
|
||||
static_assert(std::is_same_v<exp_invert_t<exp<d1, -1>>, exp<d1, 1>>);
|
||||
|
||||
// extract
|
||||
|
||||
template<typename T>
|
||||
struct typeinfo;
|
||||
|
||||
static_assert(std::is_same_v<detail::extract_t<>, dimension<>>);
|
||||
static_assert(std::is_same_v<detail::extract_t<exp<d0, 1>>, dimension<exp<d0, 1>>>);
|
||||
static_assert(std::is_same_v<detail::extract_t<exp<d0, 1>, exp<d1, 2>>, dimension<exp<d0, 1>, exp<d1, 2>>>);
|
||||
using dim0 = dimension<>;
|
||||
using dim1 = dimension<exp<d0, 1>>;
|
||||
using dim2 = dimension<exp<d0, 1>, exp<d1, 2>>;
|
||||
static_assert(std::is_same_v<detail::extract_t<exp<dim0, 2>, exp<d0, 1>>, dimension<exp<d0, 1>>>);
|
||||
static_assert(std::is_same_v<detail::extract_t<exp<dim1, 2>, exp<d0, 1>>, dimension<exp<d0, 2>, exp<d0, 1>>>);
|
||||
static_assert(std::is_same_v<detail::extract_t<exp<dim2, -2>, exp<d0, 1>, exp<d1, 2>>, dimension<exp<d0, -2>, exp<d1, -4>, exp<d0, 1>, exp<d1, 2>>>);
|
||||
|
||||
// make_dimension
|
||||
|
||||
static_assert(std::is_same_v<make_dimension_t<exp<d0, 1>>, dimension<exp<d0, 1>>>);
|
||||
|
@@ -82,8 +82,8 @@ namespace {
|
||||
std::is_same_v<type_list_split_half<type_list<int, long, double, float>>::second_list, type_list<double, float>>);
|
||||
|
||||
// type_list_merge_sorted
|
||||
inline constexpr base_dimension d0{"d0"};
|
||||
inline constexpr base_dimension d1{"d1"};
|
||||
struct d0 { static constexpr const char* value = "d0"; };
|
||||
struct d1 { static constexpr const char* value = "d1"; };
|
||||
|
||||
static_assert(std::is_same_v<type_list_merge_sorted<type_list<exp<d0, 1>>, type_list<exp<d1, 1>>, exp_less>,
|
||||
type_list<exp<d0, 1>, exp<d1, 1>>>);
|
||||
|
Reference in New Issue
Block a user