Browse Source

move much of the general headers from NiklasRosenstein/nr-cpp to this repository

master
Niklas Rosenstein 3 years ago
parent
commit
f16c415616
Signed by: NiklasRosenstein GPG Key ID: 06D269B33D25F6C6
9 changed files with 862 additions and 105 deletions
  1. + 65
    - 0
      include/NiklasRosenstein/enumerate.hpp
  2. + 95
    - 0
      include/NiklasRosenstein/filter.hpp
  3. + 184
    - 0
      include/NiklasRosenstein/iterator.hpp
  4. + 6
    - 105
      include/NiklasRosenstein/macros.h
  5. + 114
    - 0
      include/NiklasRosenstein/macros.hpp
  6. + 52
    - 0
      include/NiklasRosenstein/range.hpp
  7. + 70
    - 0
      include/NiklasRosenstein/span.hpp
  8. + 96
    - 0
      include/NiklasRosenstein/string.hpp
  9. + 180
    - 0
      include/NiklasRosenstein/typeid.hpp

+ 65
- 0
include/NiklasRosenstein/enumerate.hpp

@ -0,0 +1,65 @@
/**
* Copyright (c) 2018 Niklas Rosenstein
* MIT licensed.
*
* @description Wrapping iterator to count the iteration index during the loop.
*/
#pragma once
#include "iterator.hpp"
namespace niklasrosenstein {
/**
* Implementation of the iterator behind #enumerate().
**/
template <class Iterable>
class _enumerator : public iterator<_enumerator<Iterable>> {
Iterable iter;
std::size_t index;
decltype(std::begin(iter)) iter_begin;
decltype(std::end(iter)) const iter_end;
public:
struct item {
std::size_t index;
decltype(*iter_begin) value;
};
inline _enumerator(Iterable&& iter_)
: iter(std::move(iter_)),
index(0),
iter_begin(std::begin(iter)),
iter_end(std::end(iter))
{}
// niklasrosenstein::iterator
typedef item yield_type;
inline bool has_next() const { return this->iter_begin != this->iter_end; }
inline void next() { ++this->iter_begin; ++this->index; }
inline item operator * () const { return {this->index, *this->iter_begin}; }
};
/**
* Enumerate over an iterator.
*/
template <class Iterable>
auto enumerate(Iterable& iter) -> _enumerator<movable_iterator_wrapper<Iterable>> {
return {iterator_lvalue_wrapper<Iterable>(iter)};
}
/**
* Enumerate over an rvalue iterator.
*/
template <class Iterable>
auto enumerate(Iterable&& iter) -> _enumerator<Iterable> {
return {std::move(iter)};
}
} // namespace niklasrosenstein

+ 95
- 0
include/NiklasRosenstein/filter.hpp

@ -0,0 +1,95 @@
/**
* Copyright (c) 2018 Niklas Rosenstein
* MIT licensed.
*
* @description Provides a #filter() iterator.
*/
#pragma once
#include <functional>
#include "iterator.hpp"
namespace niklasrosenstein {
/**
* The actual iterator behind #filter().
*/
template <class T>
class _filter : public iterator<_filter<T>> {
public:
using iterator_type = decltype(std::declval<T>().begin());
using value_type = decltype(std::declval<iterator_type>().operator*());
using filter_type = std::function<bool(value_type)>;
private:
T _iter;
filter_type _func;
iterator_type _curr;
iterator_type const _end;
public:
inline _filter(T&& iter, filter_type&& func)
: _iter(std::move(iter)),
_func(std::move(func)),
_curr(_iter.begin()),
_end(_iter.end())
{}
typedef value_type yield_type;
inline bool has_next() {
// Consume the initial elements that should be filtered out.
while (_curr != _end && !_func(*_curr)) {
++_curr;
}
return _curr != _end;
}
inline void next() {
while (_curr != _end) {
++_curr;
if (_curr != _end && _func(*_curr)) {
break;
}
}
}
inline value_type operator * () const {
return *_curr;
}
};
/**
* Filters items which do not evaluate to #true when casting to #bool.
* @{
*/
template <class T>
inline _filter<T> filter(T&& iter) {
using VT = typename _filter<T>::value_type;
return filter(std::move(iter), [](VT v) { return (bool) v; });
}
template <class T>
inline _filter<movable_iterator_wrapper<T>> filter(T& iter) {
return filter(movable_iterator_wrapper<T>(iter));
}
// @}
/**
* Filters out items for which the callable #func returns false.
* @{
*/
template <class T, class F>
inline _filter<T> filter(T&& iter, F&& func) {
return {(std::move(iter)), func};
}
template <class T, class F>
inline _filter<movable_iterator_wrapper<T>> filter(T& iter, F&& func) {
return filter(movable_iterator_wrapper<T>(iter), func);
}
// @}
} // namespace niklasrosenstein

+ 184
- 0
include/NiklasRosenstein/iterator.hpp

@ -0,0 +1,184 @@
/**
* Copyright (c) 2018 Niklas Rosenstein
* MIT licensed.
*
* @description This header provides base class for implementing stateful
* iterators that are simple to implement. We call these iterators
* "new-style".
*/
#pragma once
#include <cassert>
#include <iterator>
/**
* @macro NR_ITERATOR_NOTYPETRAITS
*
* If this macro is defined, #<type_traits> will not but used. This requires
* the implementor of the #niklasrosenstein::iterator interface to manually
* specify the datatype that the `operator * ()` (dereference operator) will
* return as `yield_type`.
*/
#ifndef NR_ITERATOR_NOTYPETRAITS
#include <type_traits>
#endif
namespace niklasrosenstein {
/**
* The #self_iterator is an adapter class from a new style iterator
* interface to the C++ iterator interface. It can be subclassed to
* implement iterator objects with the following interface:
*
* - `has_next() [const]`
* - `next()`
* - `X operator * () [const]`
*
* The first two methods will be used by the #self_iterator methods
* #operator++(), #operator!=() and #operator bool() to adapt to the
* C++ iterator interface. The dereference operator is required by
* the C++ iterator interface directly.
*
* This class should be used when implementing a simple iterator.
* However, the calls to #begin() and #end() will cause a copy of
* the iterator to be created, thus it will be problematic if your
* iterator allocates and frees resources automatically. Use the
* #niklasrosenstein::iterator class instead.
*/
template <class T>
struct self_iterator {
/**
* Used by the standard C++ iterator interface. Calls `has_next()` to
* check if the end of the iterator was reached.
*/
inline bool operator != (self_iterator const&) const {
return !static_cast<T const*>(this)->has_next();
}
/**
* Increment operator. Calls `next()` to advance to the next element.
*/
inline self_iterator& operator ++ () {
static_cast<T*>(this)->next();
return *this;
}
/**
* C++ iterator interface.
* @{
*/
T& begin() { return *static_cast<T*>(this); }
T& end() { return *static_cast<T*>(this); }
// @}
};
/**
* This class is an adapter of the C++ iterator interface for new style
* iterator classes.
*/
template <typename T>
class iterator_adapter {
/* Pointer to the iterator implementation. */
T* itimpl;
public:
/**
* @typedef yield_type
*
* This type represents the datatype that is yielded by the iterator.
* If #NR_ITERATOR_NOTYPETRAITS is defined, #<type_traits> will not be
* used and #T must provide the #yield_type instead.
**/
#ifdef NR_ITERATOR_NOTYPETRAITS
typedef typename T::yield_type yield_type;
#else
typedef typename std::result_of<decltype(&T::operator *)(T)>::type yield_type;
#endif
/**
* Passing nullptr to this constructor represents creates a sentinel
* iterator that marks the end of the iteration.
*/
inline iterator_adapter(T* itimpl_) : itimpl(nullptr) {
if (itimpl_ && itimpl_->has_next()) {
this->itimpl = itimpl_;
}
}
/**
* Calls `next()` and `has_next()` on the iterator implementation to advance
* to the next item and check if the end of the iteration has been reached.
*/
inline iterator_adapter& operator ++ () {
assert(this->itimpl != nullptr);
this->itimpl->next();
if (!this->itimpl->has_next()) {
this->itimpl = nullptr;
}
return *this;
}
inline bool operator != (iterator_adapter const& other) const {
return this->itimpl != other.itimpl;
}
inline yield_type operator * () const {
assert(this->itimpl != nullptr);
return this->itimpl->operator * ();
}
};
/**
* This is an extension of the #self_iterator that uses an #iterator_adapter
* class for #begin() and #end() instead. It should be used when employing an
* iterator that allocates and frees resource dynamically.
*/
template <class T>
class iterator : public self_iterator<T> {
public:
/**
* Returns the #iterator_adapter for this iterator implementation. It does
* not really mark the begin of the iteration, rather the current state of
* the iterator.
*/
inline iterator_adapter<T> begin() { return iterator_adapter<T>(static_cast<T*>(this)); }
/**
* Returns the sentinel #iterator_adapter that marks the end of the
* iteration.
*/
inline iterator_adapter<T> end() const { return iterator_adapter<T>(nullptr); }
};
/**
* A wrapper for iterators that can be used to make your iterator compatible
* if the input iterator is not movable. This class simply wraps the non-movable
* iterator and is itself movable.
*
* template <typename Iterable>
* auto enumerate(Iterable& iter) -> _enumerator<movable_iterator_wrapper<Iterable>> {
* return {movable_iterator_wrapper<Iterable>(iter)};
* }
*/
template <typename Iterable>
class movable_iterator_wrapper {
Iterable& _iter;
public:
movable_iterator_wrapper(Iterable& iter) : _iter(iter) {}
movable_iterator_wrapper(movable_iterator_wrapper&& other)
: _iter(other._iter) {}
auto begin() -> decltype(std::begin(_iter)) { return std::begin(_iter); }
auto end() -> decltype(std::end(_iter)) { return std::end(_iter); }
auto begin() const -> decltype(std::begin(_iter)) { return std::begin(_iter); }
auto end() const -> decltype(std::end(_iter)) { return std::end(_iter); }
};
} // namespace niklasrosenstein

+ 6
- 105
include/NiklasRosenstein/macros.h

@ -1,19 +1,9 @@
/* Copyright (c) 2018 Niklas Rosenstien
* MIT licensed
*
* @description This header provides some useful macros for compiler
* and platform detection, generating compiler-based inline pragmas,
* control-flow and classes.
/**
* Copyright (c) 2018 Niklas Rosenstein
* MIT licensed.
*/
#pragma once
#include <utility> // std::move
#if !defined(HAVE_TYPE_TRAITS) || HAVE_TYPE_TRAITS
#define NR_MACROS_HAVE_TYPE_TRAITS 1
#include <type_traits>
#else
#define NR_MACROS_HAVE_TYPE_TRAITS 0
#endif
/**
* This macro allows you to create an enclosed scope that can be
@ -30,6 +20,7 @@
#define NR_SCOPE \
for (bool __nr_break=false; !__nr_break; __nr_break=true)
/**
* This macro allows you to create an enclosed scope that can be
* exited with a `break` or `continue` statement and that will
@ -46,6 +37,7 @@
#define NR_IF(condition) \
for (bool __nr_break=false; !__nr_break && (condition); __nr_break=true)
/**
* Use this macro to define a global variable or a static class
* member in a compilation unit instead of writing the actual type
@ -59,98 +51,6 @@
*/
#define NR_DECL(member) decltype(member) member
/**
* This macro can be used to disallow the copy constructor and copy
* assignment operator of a class. It must be used in the `private:`
* section of a class.
*
* class NonCopiableClass {
* NR_DISALLOW_COPY_AND_ASSIGN(NonCopiableClass);
* public:
* // ...
* };
*/
#define NR_DISALLOW_COPY_AND_ASSIGN(type) \
type(type const&); \
type& operator = (type const&)
/**
* This macro creates a copy assignment operator from an existing copy
* constructor. This is useful to avoid code duplication for copiable
* types.
*
* class CopiableClass {
* public:
* CopiableClass(CopiableClass const& other) { ... }
* NR_OPERATOR_COPY_ASSIGNMENT(CopiableClass)
* };
*/
#define NR_OPERATOR_COPY_ASSIGNMENT(type) \
type& operator = (type const& src) \
{ \
if (this != &src) \
{ \
this->~type(); \
new (this) type(src); \
} \
return *this; \
}
/**
* This macro creates a move assignment operator from an existing move
* constructor. This is useful to avoid code duplication for movable
* types.
*
* class MovableClass {
* public:
* MovableClass(MovableClass&& other) { ... }
* NR_OPERATOR_MOVE_ASSIGNMENT(MovableClass)
* };
*/
#define NR_OPERATOR_MOVE_ASSIGNMENT(type) \
type& operator = (type&& src) \
{ \
if (this != &src) \
{ \
this->~type(); \
new (this) type(std::move(src)); \
} \
return *this; \
}
/**
* Allows you to use template parametrized classes and functions as arguments
* to macros, as otherwise the comma in the template arguments would be treated
* as separators to the macro arguments. Note that this is only needed for
* templates with more than one template argument.
*
* #define SOME_MACRO(type)
* SOME_MACRO(std::vector<int, std::allocator<int>>) // error: macro "SOME_MACRO" passed 2 arguments, but takes just 1
* SOME_MACRO(NR_TYPE_IDENTITY(std::vector<int, std::allocator<int>>))
*/
#define NR_TYPE_IDENTITY(...) niklasrosenstein::detail::__type_identity_helper<void(__VA_ARGS__)>::type
namespace niklasrosenstein {
namespace detail {
template <typename T> struct __type_identity_helper;
template <typename T, typename P> struct __type_identity_helper<T(P)> { typedef P type; };
}
}
#if NR_MACROS_HAVE_TYPE_TRAITS
/**
* Converts a pointer like `T*`, converts the pointer to `T const*`.
* This is useful when implementing non-const/const versions of a
* getter function.
*
* struct foo {
* int const* find_something() const { ...; } // Imagine a long and complex method
* int* find_something() { return const_cast<int*>(NR_MAKE_CONST(this)->get_member()); }
* };
*/
#define NR_MAKE_CONST(ptr) \
static_cast<std::add_const<std::remove_pointer<decltype(ptr)>::type>::type*>(ptr)
#endif
/**
* @macro NR_INLINE_PRAGMA(...)
@ -189,6 +89,7 @@ namespace niklasrosenstein {
#define NR_INLINE_PRAGMA_MSVC(...)
#endif
/**
* @macro NR_PLATFORM
* @macro NR_PLATFORM_WINDOWS

+ 114
- 0
include/NiklasRosenstein/macros.hpp

@ -0,0 +1,114 @@
/**
* Copyright (c) 2018 Niklas Rosenstein
* MIT licensed.
*
* @description This header provides some useful macros for compiler
* and platform detection, generating compiler-based inline pragmas,
* control-flow and classes.
*/
#pragma once
#include <utility> // std::move
#if !defined(HAVE_TYPE_TRAITS) || HAVE_TYPE_TRAITS
#define NR_MACROS_HAVE_TYPE_TRAITS 1
#include <type_traits>
#else
#define NR_MACROS_HAVE_TYPE_TRAITS 0
#endif
#include "macros.h"
/**
* This macro can be used to disallow the copy constructor and copy
* assignment operator of a class. It must be used in the `private:`
* section of a class.
*
* class NonCopiableClass {
* NR_DISALLOW_COPY_AND_ASSIGN(NonCopiableClass);
* public:
* // ...
* };
*/
#define NR_DISALLOW_COPY_AND_ASSIGN(type) \
type(type const&); \
type& operator = (type const&)
/**
* This macro creates a copy assignment operator from an existing copy
* constructor. This is useful to avoid code duplication for copiable
* types.
*
* class CopiableClass {
* public:
* CopiableClass(CopiableClass const& other) { ... }
* NR_OPERATOR_COPY_ASSIGNMENT(CopiableClass)
* };
*/
#define NR_OPERATOR_COPY_ASSIGNMENT(type) \
type& operator = (type const& src) \
{ \
if (this != &src) \
{ \
this->~type(); \
new (this) type(src); \
} \
return *this; \
}
/**
* This macro creates a move assignment operator from an existing move
* constructor. This is useful to avoid code duplication for movable
* types.
*
* class MovableClass {
* public:
* MovableClass(MovableClass&& other) { ... }
* NR_OPERATOR_MOVE_ASSIGNMENT(MovableClass)
* };
*/
#define NR_OPERATOR_MOVE_ASSIGNMENT(type) \
type& operator = (type&& src) \
{ \
if (this != &src) \
{ \
this->~type(); \
new (this) type(std::move(src)); \
} \
return *this; \
}
/**
* Allows you to use template parametrized classes and functions as arguments
* to macros, as otherwise the comma in the template arguments would be treated
* as separators to the macro arguments. Note that this is only needed for
* templates with more than one template argument.
*
* #define SOME_MACRO(type)
* SOME_MACRO(std::vector<int, std::allocator<int>>) // error: macro "SOME_MACRO" passed 2 arguments, but takes just 1
* SOME_MACRO(NR_TYPE_IDENTITY(std::vector<int, std::allocator<int>>))
*/
#define NR_TYPE_IDENTITY(...) niklasrosenstein::detail::__type_identity_helper<void(__VA_ARGS__)>::type
namespace niklasrosenstein {
namespace detail {
template <typename T> struct __type_identity_helper;
template <typename T, typename P> struct __type_identity_helper<T(P)> { typedef P type; };
}
}
#if NR_MACROS_HAVE_TYPE_TRAITS
/**
* Converts a pointer like `T*`, converts the pointer to `T const*`.
* This is useful when implementing non-const/const versions of a
* getter function.
*
* struct foo {
* int const* find_something() const { ...; } // Imagine a long and complex method
* int* find_something() { return const_cast<int*>(NR_MAKE_CONST(this)->get_member()); }
* };
*/
#define NR_MAKE_CONST(ptr) \
static_cast<std::add_const<std::remove_pointer<decltype(ptr)>::type>::type*>(ptr)
#endif

+ 52
- 0
include/NiklasRosenstein/range.hpp

@ -0,0 +1,52 @@
/**
* Copyright (c) 2018 Niklas Rosenstein
* MIT licensed.
*
* @description Provides a #range() iterator.
*/
#pragma once
#include <cstddef>
#include "iterator.hpp"
namespace niklasrosenstein {
/**
* An implementation of a range iterator using the #iterator interface.
* Supports an arbitrary integer dataype that can be specified with the
* template parameter #T.
*/
template <class T = std::size_t>
class range : public iterator<range<T>> {
T _curr, _max, _step;
static inline int sign(T const& val) { return (T(0) < val) - (val < T(0)); }
public:
/**
* Create a range iterator starting from zero up excluding #max.
*/
inline range(T const& max) : _curr(0), _max(max), _step(1) { }
/**
* Create a range iterator starting from #min up to excluding #max
* taking the specified #step each turn (must not be zero and the
* sign must match the direction of the iteration).
*/
inline range(T min, T max, T step = 1) : _curr(min), _max(max), _step(step) {
assert(step != 0 && sign(step) == sign(max - min));
}
// niklasrosenstein::iterator
typedef T yield_type;
inline bool has_next() const { return this->_curr < this->_max; }
inline void next() { this->_curr += _step; }
inline T operator * () const { return this->_curr; }
};
} // namespace niklasrosenstein

+ 70
- 0
include/NiklasRosenstein/span.hpp

@ -0,0 +1,70 @@
/**
* Copyright (c) 2018 Niklas Rosenstein
* MIT licensed.
*
* @description A very simple implementation of a view on an STL container.
*/
#pragma once
#include <cassert>
namespace niklasrosenstein {
/* A view for a range of elements in a container. */
template <class C>
class span {
public:
typedef typename C::value_type value_type;
typedef typename C::size_type size_type;
typedef typename C::iterator iterator;
typedef typename C::const_iterator const_iterator;
inline span(C& container)
: container(container), start(0), max(container.size()) { }
inline span(C& container, size_type start, size_type max)
: container(container), start(start), max(max)
{
assert(start >= 0 && start <= container.size());
assert(max >= start && max <= container.size());
}
inline iterator begin() { return this->container.begin() + this->start; }
inline iterator end() { return this->container.begin() + this->max; }
inline value_type const& operator [] (size_type index) const {
assert(index >= 0 && (this->start + index) < this->max);
return this->container[this->start + index];
}
inline value_type& operator [] (size_type index) {
return const_cast<value_type&>(static_cast<span const*>(this)->operator [] (index));
}
inline size_type size() const { return (this->max - this->start); }
private:
template <typename L>
friend span<L> make_span(span<L>&, typename L::size_type, typename L::size_type);
C& container;
size_type start, max;
};
template <typename C>
span<C> make_span(C& container) {
return span<C>(container);
}
template <typename C>
span<C> make_span(C& container, typename C::size_type start, typename C::size_type max) {
return span<C>(container, start, max);
}
template <typename C>
span<C> make_span(span<C>& aspan, typename C::size_type start, typename C::size_type max) {
return span<C>(aspan.container, start + aspan.start, max + aspan.start);
}
} // namespace niklasrosenstein

+ 96
- 0
include/NiklasRosenstein/string.hpp

@ -0,0 +1,96 @@
/**
* Copyright (c) 2018 Niklas Rosenstein
* MIT licensed.
*
* @description Some useful string functions.
*/
#pragma once
#include <algorithm>
#include <cctype>
#include <functional>
#include <locale>
#include <cstring>
namespace niklasrosenstein {
/**
* Convert a string or data to a hexadecimal representation. The buffer
* must be double the size of the input data.
*/
// @{
inline void tohex(char* buffer, void const* data, size_t size) {
char const pool[] = "0123456789abcdef";
for (size_t i = 0; i < size; ++i) {
unsigned char c = reinterpret_cast<unsigned char const*>(data)[i];
buffer[i * 2 + 0] = pool[(c >> 4) & 0xf];
buffer[i * 2 + 1] = pool[(c >> 0) & 0xf];
}
}
template <class string=std::string>
inline string tohex(void const* data, size_t size) {
string r(size * 2, 0);
tohex(&r[0], data, size);
return r;
}
template <class string=std::string>
inline string tohex(const char* str) {
return tohex<string>(str, std::strlen(str));
}
template <class instring, class outstring=instring>
inline outstring tohex(instring const& str) {
return tohex<outstring>(str.c_str(), str.size());
}
// @}
/**
* Returns true if #c is a whitespace character, false if not.
* Unlike the standard #isspace(), this function does not assert whether
* a valid ASCII character was passed to it. For any character outside of
* the ASCII range, the function returns false.
*/
inline bool isspace(int chr) {
if (chr < 0 || chr > 255) return false;
else return ::isspace(chr);
}
/* Trims a string of whitespace from the left. */
template <class string>
inline string& ltrim(string& s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
std::not1(std::ptr_fun<int, int>(std::isspace))));
return s;
}
/* Trims a string of whitespace from the right. */
template <class string>
inline string& rtrim(string& s) {
s.erase(std::find_if(s.rbegin(), s.rend(),
std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
return s;
}
/* Trims a string of whitespace from both ends. */
template <class string>
inline string& trim(string& s) {
return ltrim(rtrim(s));
}
/* Converts a string to lowercase. */
template <class string>
inline string& tolower(string& s) {
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
return s;
}
/* Converts a string to uppercase. */
template <class string>
inline string& toupper(string& s) {
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
return s;
}
} // namespace niklasrosenstein

+ 180
- 0
include/NiklasRosenstein/typeid.hpp

@ -0,0 +1,180 @@
/**
* Copyright (c) 2018 Niklas Rosenstein
* MIT licensed.
*
* @description An alternative to C++ RTTI and `dynamic_cast<>()`.
*/
#pragma once
#include <type_traits>
#include "macros.hpp"
namespace niklasrosenstein {
namespace detail {
/**
* Helper structure to read the ID of a type from its `type_id` member
* using SFINAE. Thanks to T.C. on StackOverflow for this workaround.
* See http://stackoverflow.com/q/40321597/791713.
*/
struct type_id_impl {
template <class T>
static constexpr auto get(...) -> int { return 0; }
template <class T>
static constexpr auto get(int) -> decltype(T::type_id) { return T::type_id; }
};
/**
* Template that yields the ID of a type either from its `T::type_id`
* member or by a specialization.
*/
template <class T> struct type_id {
static int const value = type_id_impl::get<T>(0);
};
/**
* Implementation of the actual type-casting functionality. Wrapped in
* a struct to be able to mark all member methods friends of classes
* that implement the `NR_TYPEID()` macro.
*/
struct type_cast_impl {
/**
* A trait to enable a type #R only if type #E is defined. Used in the
* return type of a function to enable/disable the function respectively.
* Example:
*
* template <typename T>
* auto func(T& obj) -> typename enable_by<decltype(T::member), int>::type {
* return obj.member();
* }
*/
template <class E, class R> struct enable_by {
using type = R;
};
/**
* Plain check if the requested #target_id matches the current class
* of the #ptr. Returns `nullptr` if they do not match.
*/
template <class C>
static void* cast(C const* ptr, int target_id) {
if (type_id<C>::value == target_id) {
return static_cast<void*>(const_cast<C*>(ptr));
}
return nullptr;
}
/**
* Used if the current base-class #B supports the `__nonvirtual_cast()`
* member function (see #NR_TYPE_ID() macro).
*/
template <class C, class B, class... Bases>
static auto cast(C const* ptr, int target_id)
-> typename enable_by<decltype(B::__nonvirtual_cast), void*>::type
{
void* result = static_cast<B const*>(ptr)->__nonvirtual_cast(target_id);
if (result) return result;
return cast<C, Bases...>(ptr, target_id);
}
/**
* Used when the current base-class #B does not support the
* `__nonvirtual_cast()` member function (for types associated with
* #NR_TYPE_IDEX()).
*/
template <class C, class B, class... Bases>
static void* cast(C const* ptr, int target_id) {
void* result = cast<B>(ptr, target_id);
if (result) return result;
return cast<C, Bases...>(ptr, target_id);
}
};
} // namespace detail
/**
* Returns the ID of a type that implements the `NR_TYPEID()` macro or
* for which there exists a specialization with `NR_TYPEID_EX()`.
*/
template <class T> inline constexpr int type_id() {
return detail::type_id<T>::value;
}
/**
* This macro makes a class compatible to the type casting system provided
* with this lirbary. It assigns a `type_id` member and creates two functions
* named `__virtual_cast()` and `__nonvirtual_cast()` as well as some required
* friend declarations to allow the macro be placed in the private section.
*/
#define NR_TYPEID(cls, id, ...) \
template <class _TYPEID_R, class _TYPEID_T> friend _TYPEID_R const* niklasrosenstein::type_cast(_TYPEID_T const*); \
template <class _TYPEID_R, class _TYPEID_T> friend _TYPEID_R* niklasrosenstein::type_cast(_TYPEID_T*); \
friend struct niklasrosenstein::detail::type_id_impl; \
friend struct niklasrosenstein::detail::type_cast_impl; \
static const int type_id = (id); \
void* __nonvirtual_cast(int target_id) const { \
return niklasrosenstein::detail::type_cast_impl::cast<cls, ##__VA_ARGS__>(this, target_id); \
} \
NR_INLINE_PRAGMA_CLANG(clang diagnostic push); \
NR_INLINE_PRAGMA_CLANG(clang diagnostic ignored "-Wunknown-pragmas"); \
NR_INLINE_PRAGMA_CLANG(clang diagnostic ignored "-Winconsistent-missing-override"); \
virtual void* __virtual_cast(int target_id) const { \
return this->__nonvirtual_cast(target_id); \
} \
NR_INLINE_PRAGMA_CLANG(clang diagnostic pop);
/**
* Associate an external class with a type ID. This allows you to assign
* a type ID to a type that does not provide a `type_id` member.
*/
#define NR_TYPEID_EX(type, id) \
namespace nr { \
namespace detail { \
template <> struct type_id<type> { \
static int const value = (id); \
}; \
} \
}
/**
* Dynamically cast an object to another type. If the object does not
* support that type, `nullptr` is returned instead. Note that at least
* the type #T must have the #NR_TYPE_ID() macro in its class definition.
* @{
*/
template <class R, class T>
R const* type_cast(T const* ptr) {
return reinterpret_cast<R const*>(ptr->__virtual_cast(type_id<R>()));
}
template <class R, class T>
R* type_cast(T* ptr) {
return reinterpret_cast<R*>(ptr->__virtual_cast(type_id<R>()));
}
// @}
/**
* Adapter and automatic caster for objects that support a specific
* interface. Represents a pointer to that object. If the constructor
* can not cast the object to the type #T, the #type_adapter object
* will be filled with a nullptr.
*/
template <class T>
class type_adapter {
T* ptr;
public:
type_adapter(T* ptr_=nullptr) : ptr(ptr_) {}
type_adapter(type_adapter const& other) : ptr(other.ptr) {}
type_adapter(type_adapter&& other) : ptr(other.ptr) { other.ptr = nullptr; }
type_adapter& operator = (T* ptr_) { this->ptr = ptr; return *this; }
template <typename R>
type_adapter(R* ptr_) : ptr(type_cast<T>(ptr)) {}
operator T* () const { return this->ptr; }
T* operator -> () const { return this->ptr; }
};
} // namespace niklasrosenstein

Loading…
Cancel
Save