mirror of
https://github.com/boostorg/type_index.git
synced 2025-07-29 20:07:18 +02:00
Fix some of the notes mentioned by Andrey Semashev (more to come)
This commit is contained in:
@ -33,7 +33,7 @@ Boost.TypeIndex library was designed to work around all those issues.
|
||||
|
||||
[classref boost::typeindex::type_info boost::typeindex::type_info] is a drop-in replacement for `std::type_info` and
|
||||
[classref boost::typeindex::type_index boost::typeindex::type_index]
|
||||
is a drop-in replacement for `std::type_index`. Unlike Standard Library versions those classes may work without RTTI.
|
||||
is a drop-in replacement for `std::type_index`. Unlike Standard Library versions those classes can work without RTTI.
|
||||
|
||||
`type_index` provides the full set of comparison operators, hashing functions and ostream
|
||||
operators, so it can be used with any container class.
|
||||
@ -228,7 +228,7 @@ Issues with cross module type comparison on a bugged compilers are bypassed by d
|
||||
[section Getting through the inheritance to receive a real type name ] [type_index_derived_example] [endsect]
|
||||
|
||||
[import ../examples/exact_types_match.cpp]
|
||||
[section Exact type match: storing type with const, volatile and reference ] [type_index_exact_type_match_example] [endsect]
|
||||
[section Exact type matching: storing type with const, volatile and reference qualifiers] [type_index_exact_type_match_example] [endsect]
|
||||
|
||||
[import ../examples/table_of_names.cpp]
|
||||
[section Table of raw_name() and pretty_name() outputs with and without RTTI ] [type_index_names_table] [endsect]
|
||||
@ -354,7 +354,7 @@ and `BOOST_TYPE_INDEX_CTTI_END_SKIP` to `sizeof(">::n(void)") - 1`.
|
||||
|
||||
Linking a binary from source files that were compiled with different RTTI flags is not a very good
|
||||
idea and may lead to a lot of surprises. However if there is a very strong need, TypeIndex library
|
||||
provides a solution for mixing sources: just define `BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY`
|
||||
provides a solution for mixing sources: just define [macroref BOOST_TYPE_INDEX_FORCE_NO_RTTI_COMPATIBILITY]
|
||||
macro. This would lead to usage of same type_index class (`boost::typeindex::ctti_type_index` or
|
||||
`boost::typeindex::stl_type_index`) all around the project.
|
||||
|
||||
|
@ -9,9 +9,9 @@
|
||||
The following example shows that `type_index` (and `type_info`) is able to store the exact type,
|
||||
without stripping const, volatile and references. Example works with and without RTTI.
|
||||
|
||||
In this example we'll create a class, that stores pointer to function and remembers the exact type of a
|
||||
parameter that function accepts. When an attempt to call the stored function will be made, type of input
|
||||
parameter will be checked for exact match with initially erased type of function.
|
||||
In this example we'll create a class that stores a pointer to function and remembers the exact type of the
|
||||
parameter the function accepts. When the call to the bound function is made, he actual input parameter
|
||||
type is checked against the stored parameter type and an exception is thrown in case of mismatch.
|
||||
*/
|
||||
|
||||
#include <boost/type_index.hpp>
|
||||
@ -20,8 +20,8 @@
|
||||
#include <cassert>
|
||||
|
||||
class type_erased_unary_function {
|
||||
void* function_ptr_;
|
||||
boost::typeindex::type_index exact_param_t_;
|
||||
void* function_ptr_;
|
||||
boost::typeindex::type_index exact_param_t_;
|
||||
|
||||
public:
|
||||
template <class ParamT>
|
||||
|
@ -14,11 +14,6 @@
|
||||
/// By inclusion of this file most optimal type index classes will be included and used
|
||||
/// as a boost::typeindex::type_index and boost::typeindex::type_info.
|
||||
|
||||
// MS compatible compilers support #pragma once
|
||||
#if defined(_MSC_VER)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#if defined(BOOST_TYPE_INDEX_USER_TYPEINDEX)
|
||||
@ -33,6 +28,10 @@
|
||||
# include <boost/type_index/ctti_register_class.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef BOOST_HAS_PRAGMA_ONCE
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
namespace boost { namespace typeindex {
|
||||
|
||||
#if defined(BOOST_TYPE_INDEX_DOXYGEN_INVOKED)
|
||||
@ -82,7 +81,7 @@ typedef type_index::type_info_t type_info;
|
||||
/// Put this macro into the public section of polymorphic class to allow runtime type detection.
|
||||
///
|
||||
/// Depending on the typeid() availability this macro will expand to nothing or to virtual helper function
|
||||
/// `virtual const type_info& type_id_runtime() const`.
|
||||
/// `virtual const type_info& boost_type_info_type_id_runtime_() const noexcept`.
|
||||
///
|
||||
/// \b Example:
|
||||
/// \code
|
||||
|
@ -9,16 +9,15 @@
|
||||
#ifndef BOOST_TYPE_INDEX_CTTI_REGISTER_CLASS_HPP
|
||||
#define BOOST_TYPE_INDEX_CTTI_REGISTER_CLASS_HPP
|
||||
|
||||
// MS compatible compilers support #pragma once
|
||||
#if defined(_MSC_VER)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
/// \file ctti_register_class.hpp
|
||||
/// \brief Contains BOOST_TYPE_INDEX_REGISTER_CTTI_CLASS macro.
|
||||
|
||||
#include <boost/type_index/ctti_type_index.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_PRAGMA_ONCE
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
namespace boost { namespace typeindex { namespace detail {
|
||||
|
||||
template <class T>
|
||||
@ -33,11 +32,11 @@ inline const ctti_data& ctti_construct_typeid_ref(const T*) BOOST_NOEXCEPT {
|
||||
/// and `typeid()` does not work.
|
||||
///
|
||||
/// BOOST_TYPE_INDEX_REGISTER_CTTI_CLASS macro expands to declaration and implementation of
|
||||
/// `virtual const detail::ctti_data& type_id_runtime() const` method.
|
||||
#define BOOST_TYPE_INDEX_REGISTER_CTTI_CLASS \
|
||||
virtual const boost::typeindex::detail::ctti_data& type_id_runtime() const BOOST_NOEXCEPT { \
|
||||
return boost::typeindex::detail::ctti_construct_typeid_ref(this); \
|
||||
} \
|
||||
/// `virtual const type_info& boost_type_info_type_id_runtime_() const noexcept` method.
|
||||
#define BOOST_TYPE_INDEX_REGISTER_CTTI_CLASS \
|
||||
virtual const boost::typeindex::detail::ctti_data& boost_type_index_type_id_runtime_() const BOOST_NOEXCEPT { \
|
||||
return boost::typeindex::detail::ctti_construct_typeid_ref(this); \
|
||||
} \
|
||||
/**/
|
||||
|
||||
#endif // BOOST_TYPE_INDEX_CTTI_REGISTER_CLASS_HPP
|
||||
|
@ -9,11 +9,6 @@
|
||||
#ifndef BOOST_TYPE_INDEX_CTTI_TYPE_INDEX_HPP
|
||||
#define BOOST_TYPE_INDEX_CTTI_TYPE_INDEX_HPP
|
||||
|
||||
// MS compatible compilers support #pragma once
|
||||
#if defined(_MSC_VER)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
/// \file ctti_type_index.hpp
|
||||
/// \brief Contains boost::typeindex::ctti_type_index class.
|
||||
///
|
||||
@ -31,6 +26,10 @@
|
||||
#include <boost/type_traits/remove_cv.hpp>
|
||||
#include <boost/type_traits/remove_reference.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_PRAGMA_ONCE
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
namespace boost { namespace typeindex {
|
||||
|
||||
namespace detail {
|
||||
@ -42,12 +41,33 @@ namespace detail {
|
||||
// 4) we need a compile-time control to make shure that user does not copy or
|
||||
// default construct `struct-that-represents-type`
|
||||
//
|
||||
// Solution would be a standard-layout class with private constructors and assignment operators.
|
||||
// Solution would be the following:
|
||||
|
||||
/// \class ctti_data
|
||||
/// Standard-layout class with private constructors and assignment operators.
|
||||
///
|
||||
/// You can not work with this class directly. The purpose of this class is to hold type info
|
||||
/// \b when \b RTTI \b is \b off and allow ctti_type_index construction from itself.
|
||||
///
|
||||
/// \b Example:
|
||||
/// \code
|
||||
/// const detail::ctti_data& foo();
|
||||
/// ...
|
||||
/// type_index ti = type_index(foo());
|
||||
/// std::cout << ti.pretty_name();
|
||||
/// \endcode
|
||||
class ctti_data {
|
||||
#ifndef BOOST_NO_CXX11_DELETED_FUNCTIONS
|
||||
public:
|
||||
ctti_data() = delete;
|
||||
ctti_data(const ctti_data&) = delete;
|
||||
ctti_data& operator=(const ctti_data&) = delete;
|
||||
#else
|
||||
private:
|
||||
ctti_data();
|
||||
ctti_data(const ctti_data&);
|
||||
ctti_data& operator=(const ctti_data&);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
@ -77,6 +97,10 @@ inline const detail::ctti_data& ctti_construct() BOOST_NOEXCEPT {
|
||||
class ctti_type_index: public type_index_facade<ctti_type_index, detail::ctti_data> {
|
||||
const detail::ctti_data* data_;
|
||||
|
||||
inline std::size_t get_raw_name_length() const BOOST_NOEXCEPT {
|
||||
return std::strlen(raw_name() + detail::ctti_skip_size_at_end);
|
||||
}
|
||||
|
||||
public:
|
||||
typedef detail::ctti_data type_info_t;
|
||||
|
||||
@ -126,7 +150,7 @@ inline ctti_type_index ctti_type_index::type_id_with_cvr() BOOST_NOEXCEPT {
|
||||
|
||||
template <class T>
|
||||
inline ctti_type_index ctti_type_index::type_id_runtime(const T& variable) BOOST_NOEXCEPT {
|
||||
return variable.type_id_runtime();
|
||||
return variable.boost_type_index_type_id_runtime_();
|
||||
}
|
||||
|
||||
|
||||
@ -136,14 +160,14 @@ inline const char* ctti_type_index::raw_name() const BOOST_NOEXCEPT {
|
||||
|
||||
|
||||
inline std::string ctti_type_index::pretty_name() const {
|
||||
std::size_t len = std::strlen(raw_name() + detail::ctti_skip_size_at_end);
|
||||
std::size_t len = get_raw_name_length();
|
||||
while (raw_name()[len - 1] == ' ') --len; // MSVC sometimes adds whitespaces
|
||||
return std::string(raw_name(), len);
|
||||
}
|
||||
|
||||
|
||||
inline std::size_t ctti_type_index::hash_code() const BOOST_NOEXCEPT {
|
||||
return boost::hash_range(raw_name(), raw_name() + std::strlen(raw_name() + detail::ctti_skip_size_at_end));
|
||||
return boost::hash_range(raw_name(), raw_name() + get_raw_name_length());
|
||||
}
|
||||
|
||||
|
||||
|
@ -9,16 +9,15 @@
|
||||
#ifndef BOOST_TYPE_INDEX_STL_REGISTER_CLASS_HPP
|
||||
#define BOOST_TYPE_INDEX_STL_REGISTER_CLASS_HPP
|
||||
|
||||
// MS compatible compilers support #pragma once
|
||||
#if defined(_MSC_VER)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
/// \file stl_register_class.hpp
|
||||
/// \brief Contains BOOST_TYPE_INDEX_REGISTER_STL_CLASS macro.
|
||||
|
||||
#include <boost/type_index/stl_type_index.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_PRAGMA_ONCE
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
namespace boost { namespace typeindex { namespace detail {
|
||||
|
||||
template <class T>
|
||||
@ -34,11 +33,11 @@ inline const stl_type_index::type_info_t& stl_construct_typeid_ref(const T*) BOO
|
||||
/// and `typeid()` does work.
|
||||
///
|
||||
/// BOOST_TYPE_INDEX_REGISTER_STL_CLASS macro expands to declaration and implementation of
|
||||
/// `virtual const std::type_info& type_id_runtime() const` method.
|
||||
#define BOOST_TYPE_INDEX_REGISTER_STL_CLASS \
|
||||
virtual const boost::typeindex::stl_type_index::type_info_t& type_id_runtime() const BOOST_NOEXCEPT { \
|
||||
return boost::typeindex::detail::stl_construct_typeid_ref(this); \
|
||||
} \
|
||||
/// `virtual const type_info& boost_type_info_type_id_runtime_() const noexcept` method.
|
||||
#define BOOST_TYPE_INDEX_REGISTER_STL_CLASS \
|
||||
virtual const boost::typeindex::stl_type_index::type_info_t& boost_type_index_type_id_runtime_() const BOOST_NOEXCEPT { \
|
||||
return boost::typeindex::detail::stl_construct_typeid_ref(this); \
|
||||
} \
|
||||
/**/
|
||||
|
||||
#endif // BOOST_TYPE_INDEX_STL_REGISTER_CLASS_HPP
|
||||
|
@ -9,11 +9,6 @@
|
||||
#ifndef BOOST_TYPE_INDEX_STL_TYPE_INDEX_HPP
|
||||
#define BOOST_TYPE_INDEX_STL_TYPE_INDEX_HPP
|
||||
|
||||
// MS compatible compilers support #pragma once
|
||||
#if defined(_MSC_VER)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
/// \file stl_type_index.hpp
|
||||
/// \brief Contains boost::typeindex::stl_type_index class.
|
||||
///
|
||||
@ -26,7 +21,6 @@
|
||||
|
||||
#include <boost/type_index/type_index_facade.hpp>
|
||||
|
||||
|
||||
// MSVC is capable of calling typeid(T) even when RTTI is off
|
||||
#if defined(BOOST_NO_RTTI) && !defined(BOOST_MSVC)
|
||||
#error "File boost/type_index/stl_type_index.ipp is not usable when typeid() is not available."
|
||||
@ -41,6 +35,7 @@
|
||||
#include <boost/type_traits/remove_cv.hpp>
|
||||
#include <boost/type_traits/remove_reference.hpp>
|
||||
#include <boost/mpl/if.hpp>
|
||||
#include <boost/mpl/or.hpp>
|
||||
#include <boost/functional/hash_fwd.hpp>
|
||||
|
||||
|
||||
@ -51,6 +46,7 @@
|
||||
#if !defined(BOOST_MSVC)
|
||||
# include <boost/assert.hpp>
|
||||
# include <boost/detail/no_exceptions_support.hpp>
|
||||
# include <cstdlib> // std::free
|
||||
#endif
|
||||
|
||||
#if (defined(__EDG_VERSION__) && __EDG_VERSION__ < 245) \
|
||||
@ -58,6 +54,10 @@
|
||||
# include <boost/type_traits/is_arithmetic.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef BOOST_HAS_PRAGMA_ONCE
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
namespace boost { namespace typeindex {
|
||||
|
||||
/// \class stl_type_index
|
||||
@ -143,11 +143,11 @@ inline std::string stl_type_index::pretty_name() const {
|
||||
BOOST_TRY {
|
||||
ret = demang; // may throw out of memory exception
|
||||
} BOOST_CATCH (...) {
|
||||
free(demang);
|
||||
std::free(demang);
|
||||
BOOST_RETHROW;
|
||||
} BOOST_CATCH_END
|
||||
|
||||
free(demang);
|
||||
std::free(demang);
|
||||
#endif
|
||||
|
||||
std::string::size_type pos = ret.find("boost::typeindex::detail::cvr_saver<");
|
||||
@ -234,10 +234,8 @@ namespace detail {
|
||||
|
||||
template <class T>
|
||||
inline stl_type_index stl_type_index::type_id_with_cvr() BOOST_NOEXCEPT {
|
||||
typedef typename boost::mpl::if_c<
|
||||
boost::is_reference<T>::value
|
||||
|| boost::is_const<T>::value
|
||||
|| boost::is_volatile<T>::value,
|
||||
typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_<
|
||||
boost::mpl::or_<boost::is_reference<T>, boost::is_const<T>, boost::is_volatile<T> >,
|
||||
detail::cvr_saver<T>,
|
||||
T
|
||||
>::type type;
|
||||
@ -249,7 +247,7 @@ inline stl_type_index stl_type_index::type_id_with_cvr() BOOST_NOEXCEPT {
|
||||
template <class T>
|
||||
inline stl_type_index stl_type_index::type_id_runtime(const T& value) BOOST_NOEXCEPT {
|
||||
#ifdef BOOST_NO_RTTI
|
||||
return value.type_id_runtime();
|
||||
return value.boost_type_index_type_id_runtime_();
|
||||
#else
|
||||
return typeid(value);
|
||||
#endif
|
||||
|
@ -9,11 +9,6 @@
|
||||
#ifndef BOOST_TYPE_INDEX_TYPE_INDEX_FACADE_HPP
|
||||
#define BOOST_TYPE_INDEX_TYPE_INDEX_FACADE_HPP
|
||||
|
||||
// MS compatible compilers support #pragma once
|
||||
#if defined(_MSC_VER)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/functional/hash_fwd.hpp>
|
||||
#include <string>
|
||||
@ -27,6 +22,10 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef BOOST_HAS_PRAGMA_ONCE
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
namespace boost { namespace typeindex {
|
||||
|
||||
/// \class type_index_facade
|
||||
@ -65,46 +64,50 @@ private:
|
||||
public:
|
||||
typedef TypeInfo type_info_t;
|
||||
|
||||
#if defined(BOOST_TYPE_INDEX_DOXYGEN_INVOKED)
|
||||
/// \b Override: This function \b must be redefined in Derived class. Overrides \b must not throw.
|
||||
/// \return Const reference to underlying low level type_info_t.
|
||||
inline const type_info_t& type_info() const BOOST_NOEXCEPT {
|
||||
return derived().type_info();
|
||||
}
|
||||
inline const type_info_t& type_info() const BOOST_NOEXCEPT;
|
||||
|
||||
/// \b Override: This function \b must be redefined in Derived class. Overrides \b must not throw.
|
||||
/// \return Pointer to unredable/raw type name.
|
||||
inline const char* raw_name() const BOOST_NOEXCEPT {
|
||||
inline const char* raw_name() const BOOST_NOEXCEPT;
|
||||
#endif
|
||||
|
||||
/// \b Override: This function \b may be redefined in Derived class. Overrides \b must not throw.
|
||||
/// \return Name of a type. By default retuns Derived::raw_name().
|
||||
inline const char* name() const BOOST_NOEXCEPT {
|
||||
return derived().raw_name();
|
||||
}
|
||||
|
||||
/// \b Override: This function \b must be redefined in Derived class. Overrides may throw.
|
||||
/// \return Human redable type name.
|
||||
/// \b Override: This function \b may be redefined in Derived class. Overrides may throw.
|
||||
/// \return Human redable type name. By default retuns Derived::name().
|
||||
inline std::string pretty_name() const {
|
||||
return derived().pretty_name();
|
||||
}
|
||||
|
||||
/// \b Override: This function \b may be redefined in Derived class. Overrides \b must not throw.
|
||||
/// \return Name of a type. By default retuns raw_name().
|
||||
inline const char* name() const BOOST_NOEXCEPT {
|
||||
return raw_name();
|
||||
return derived().name();
|
||||
}
|
||||
|
||||
/// \b Override: This function \b may be redefined in Derived class. Overrides \b must not throw.
|
||||
/// \return True if two types are equal. By default compares types by raw_name().
|
||||
inline bool equal(const Derived& rhs) const BOOST_NOEXCEPT {
|
||||
return raw_name() == rhs.raw_name() || !std::strcmp(raw_name(), rhs.raw_name());
|
||||
const char* const left = derived().raw_name();
|
||||
const char* const right = rhs.raw_name();
|
||||
return left == right || !std::strcmp(left, right);
|
||||
}
|
||||
|
||||
/// \b Override: This function \b may be redefined in Derived class. Overrides \b must not throw.
|
||||
/// \return True if rhs is greater than this. By default compares types by raw_name().
|
||||
inline bool before(const Derived& rhs) const BOOST_NOEXCEPT {
|
||||
return raw_name() != rhs.raw_name() && std::strcmp(raw_name(), rhs.raw_name()) < 0;
|
||||
const char* const left = derived().raw_name();
|
||||
const char* const right = rhs.raw_name();
|
||||
return left != right && std::strcmp(left, right) < 0;
|
||||
}
|
||||
|
||||
/// \b Override: This function \b may be redefined in Derived class. Overrides \b must not throw.
|
||||
/// \return Hash code of a type. By default hashes types by raw_name().
|
||||
/// \note <boost/functional/hash.hpp> has to be included if this function is used.
|
||||
inline std::size_t hash_code() const BOOST_NOEXCEPT {
|
||||
return boost::hash_range(raw_name(), raw_name() + std::strlen(raw_name()));
|
||||
const char* const name = derived().raw_name();
|
||||
return boost::hash_range(name, name + std::strlen(name));
|
||||
}
|
||||
|
||||
#if defined(BOOST_TYPE_INDEX_DOXYGEN_INVOKED)
|
||||
@ -260,7 +263,7 @@ bool operator ==, !=, <, ... (const TypeInfo& lhs, const type_index_facade& rhs)
|
||||
/// Ostream operator that will output demangled name
|
||||
template <class Derived, class TypeInfo>
|
||||
inline std::ostream& operator<<(std::ostream& ostr, const type_index_facade<Derived, TypeInfo>& ind) {
|
||||
ostr << ind.pretty_name();
|
||||
ostr << static_cast<Derived const&>(ind).pretty_name();
|
||||
return ostr;
|
||||
}
|
||||
/// @endcond
|
||||
@ -271,13 +274,14 @@ inline std::basic_ostream<CharT, TriatT>& operator<<(
|
||||
std::basic_ostream<CharT, TriatT>& ostr,
|
||||
const type_index_facade<Derived, TypeInfo>& ind)
|
||||
{
|
||||
ostr << ind.pretty_name();
|
||||
ostr << static_cast<Derived const&>(ind).pretty_name();
|
||||
return ostr;
|
||||
}
|
||||
#endif // BOOST_NO_TEMPLATED_IOSTREAMS
|
||||
#endif // BOOST_NO_IOSTREAM
|
||||
|
||||
/// This free function is used by Boost's unordered containers.
|
||||
/// \note <boost/functional/hash.hpp> has to be included if this function is used.
|
||||
template <class Derived, class TypeInfo>
|
||||
inline std::size_t hash_value(const type_index_facade<Derived, TypeInfo>& lhs) BOOST_NOEXCEPT {
|
||||
return static_cast<Derived const&>(lhs).hash_code();
|
||||
|
@ -297,7 +297,7 @@ BOOST_AUTO_TEST_CASE(comparators_type_id_runtime)
|
||||
BOOST_CHECK(typeid(&rc1) == typeid(pb1));
|
||||
BOOST_CHECK(typeid(&rb1) == typeid(pc1));
|
||||
#else
|
||||
BOOST_CHECK(boost::typeindex::type_index(pc1->type_id_runtime()).raw_name());
|
||||
BOOST_CHECK(boost::typeindex::type_index(pc1->boost_type_index_type_id_runtime_()).raw_name());
|
||||
#endif
|
||||
|
||||
BOOST_CHECK_EQUAL(boost::typeindex::type_id_runtime(rc1), boost::typeindex::type_id_runtime(*pc1));
|
||||
|
Reference in New Issue
Block a user