Skip to content

Instantly share code, notes, and snippets.

@Redchards
Created December 17, 2015 21:06
Show Gist options
  • Select an option

  • Save Redchards/9bcdca8f42dcc02fb758 to your computer and use it in GitHub Desktop.

Select an option

Save Redchards/9bcdca8f42dcc02fb758 to your computer and use it in GitHub Desktop.
Straightforward CTTI (compile time type information) implementation, based on the Eric Niebler's one, using my string classes (C++14)
#pragma once
#include <cstddef>
#include <stdexcept>
//#include <Configuration.h>
#include "Platform.hxx"
// There's a warning about "multiple copy constructor specified", due to partial visual studio
// SFINAE implementation.
// We can safely silence them out, as it should not be like this.
// The warning is due to the fact that a non-const iterator can be copied to a const iterator, but not
// the other way. So, a non-const iterator has a copy constructor AND a constructor accepting a const
// iterator argument. But for const iterator class, this constructor is disabled.
// It does work as expected in clang and gcc, but not un visual studio yet. At least, this is only
// a warning and not an error (constructor implementations are the same anyway).
#if(COMPILER == MVSC_COMPILER)
#pragma warning(push)
#pragma warning(disable : 4521)
#endif
template<class ArrayClass>
class ArrayIteratorPolicy
{
protected:
template<bool constFlag>
class BaseIterator
{
friend BaseIterator<!constFlag>;
protected:
using indexType = typename std::conditional<constFlag, const size_t, size_t>::type;
public:
using value_type = typename ArrayClass::value_type;
using reference = typename std::conditional<constFlag, const typename std::remove_cv<value_type>::type&, value_type&>::type;
using pointer = typename std::conditional<constFlag, const typename std::remove_cv<value_type>::type*, value_type*>::type;
using difference = ptrdiff_t;
public:
constexpr BaseIterator() = delete;
constexpr BaseIterator(const ArrayClass& rhs, indexType index) : it_(rhs), index_(index) {}
constexpr BaseIterator(const BaseIterator& other) : it_(other.it_),
index_(other.index_)
{}
template<bool TconstFlag = constFlag,
typename std::enable_if<TconstFlag == true, bool>::type = false>
constexpr BaseIterator(const BaseIterator<false>& other) : it_(other.it_),
index_(other.index_)
{}
template<bool TconstFlag>
constexpr bool operator==(const BaseIterator<TconstFlag>& rhs) const noexcept { return ((index_ == rhs.index_) && (it_ == rhs.it_)); }
template<bool TconstFlag>
constexpr bool operator!=(const BaseIterator<TconstFlag>& rhs) const noexcept { return !(*this == rhs); }
constexpr typename std::remove_cv<value_type>::type operator*() const { return it_[index_]; }
constexpr pointer operator->() const { return &(it_[index_]); }
constexpr operator BaseIterator<true>() const noexcept
{
return{ it_, index_ };
}
template<bool constFlag1, bool constFlag2>
friend constexpr ptrdiff_t operator-(const BaseIterator<constFlag1>& lhs, const BaseIterator<constFlag2>& rhs)
{
return (&(lhs.it_[lhs.index_]) - &(rhs.it_[rhs.index_]));
}
protected:
const ArrayClass& it_;
indexType index_;
};
template<bool constFlag>
class ReverseIterator;
template<bool constFlag>
class Iterator : public BaseIterator<constFlag>
{
protected:
using Base = BaseIterator<constFlag>;
using indexType = typename Base::indexType;
friend Iterator<!constFlag>;
public:
using value_type = typename Base::value_type;
using reference = typename Base::reference;
using pointer = typename Base::pointer;
public:
constexpr Iterator(const ArrayClass& rhs, indexType index) noexcept : Base(rhs, index)
{}
constexpr Iterator(const Iterator& other) noexcept : Base(other)
{}
template<bool TconstFlag = constFlag,
typename std::enable_if<TconstFlag == true, bool>::type = false>
constexpr Iterator(const Iterator<false>& other) noexcept : Base(other)
{}
constexpr Iterator(const ReverseIterator<constFlag>& other) noexcept : Base(other)
{}
template<bool TconstFlag = constFlag,
typename std::enable_if<TconstFlag == true, bool>::type = false>
constexpr Iterator(const ReverseIterator<false>& other) noexcept : Base(other)
{}
constexpr Iterator& operator++() noexcept
{
++(this->index_);
return *this;
}
constexpr Iterator operator++(int) noexcept
{
Iterator tmp{ *this };
++(this->index_);
return tmp;
}
constexpr Iterator& operator--() noexcept
{
--(this->index_);
return *this;
}
constexpr Iterator operator--(int) noexcept
{
Iterator tmp{ *this };
--(this->index_);
return tmp;
}
constexpr Iterator& operator+(size_t n) noexcept
{
this->index_ += n;
return *this;
}
constexpr Iterator& operator-(size_t n) noexcept
{
this->index_ -= n;
return this;
}
};
template<bool constFlag>
class ReverseIterator : public Iterator<constFlag>
{
protected:
using Base = Iterator<constFlag>;
using indexType = typename Base::indexType;
friend ReverseIterator<!constFlag>;
public:
using value_type = typename Base::value_type;
using reference = typename Base::reference;
using pointer = typename Base::pointer;
public:
// Right now, this constructor do not work (visual studio 2015 update 1). I guess it's again the bad implementation of templates
// and two pass lookup that do not play in our favor
//constexpr ReverseIterator(const ArrayClass& rhs, indexType index) noexcept : Base(rhs, index) {}
constexpr ReverseIterator(const ReverseIterator& other) noexcept : Base(other)
{}
template<bool TconstFlag = constFlag,
typename std::enable_if<TconstFlag == true, bool>::type = false>
constexpr ReverseIterator(const ReverseIterator<false>& other) noexcept : Base(other.it_, other.index_)
{}
constexpr ReverseIterator(const Iterator<constFlag>& other) noexcept : Base(other)
{}
template<bool TconstFlag = constFlag,
typename std::enable_if<TconstFlag == true, bool>::type = false>
constexpr ReverseIterator(const Iterator<false>& other) noexcept : Base(other)
{}
constexpr ReverseIterator& operator++() noexcept
{
return static_cast<ReverseIterator&>(Base::operator--());
}
constexpr ReverseIterator operator++(int) noexcept
{
return Base::operator--(0);
}
constexpr ReverseIterator& operator--() noexcept
{
return static_cast<ReverseIterator&>(Base::operator++());
}
constexpr ReverseIterator operator--(int) noexcept
{
return Base::operator++(0);
}
constexpr ReverseIterator& operator+(size_t n) noexcept
{
return static_cast<ReverseIterator&>(static_cast<Base&>(this) - n);
}
constexpr ReverseIterator& operator-(size_t n) noexcept
{
return static_cast<ReverseIterator&>(static_cast<Base&>(this) + n);
}
constexpr typename std::remove_cv<value_type>::type operator*() const { return this->it_[this->index_ - 1]; }
constexpr pointer operator->() const { return &(this->it_[this->index_ - 1]); }
};
public:
using iterator = Iterator<false>;
using reverse_iterator = ReverseIterator<false>;
using const_iterator = Iterator<true>;
using const_reverse_iterator = ReverseIterator<true>;
};
#if(COMPILER == MVSC_COMPILER)
#pragma warning(pop)
#endif
class ConstString
{
using IterPolicy = ArrayIteratorPolicy<ConstString>;
public:
using value_type = const char;
using reference = const char&;
using pointer = const char*;
public:
// Serve for the sole purpose of begin able to be literal type even with default constructor
ConstString() = default;
constexpr ConstString(const ConstString& str) = default;
template<size_t size>
constexpr ConstString(const char(&cstr)[size]) noexcept : size_(size - 1), cstr_(cstr)
{}
constexpr char at(size_t index) const
{
return (index < size_ ? cstr_[index] : throw std::out_of_range("Attempt to access a non-existing index of a constant string"));
}
constexpr char operator[](size_t index) const
{
return at(index);
}
// Modified to build with visual studio. Gcc and clang do not need recursion, simple if branches and for loop are working.
friend constexpr bool operator==(const ConstString& lhs, const ConstString& rhs)
{
return lhs.size() != rhs.size() ? false : equalAux(lhs.size(), lhs, rhs);
}
/* Alternative definition, working on GCC 5.2.0 and Clang 3.7
It also avoid the recursion depth problem with big strings
constexpr bool operator==(const ConstString& rhs) const
{
if(size_ != rhs.size()) return false;
for(uint8_t i = 0; i < size_; ++i)
{
if((*this)[i] != rhs[i])
{
return false;
}
}
return true;
}
*/
constexpr size_t size() const noexcept { return size_; }
constexpr pointer data() const noexcept { return cstr_; }
constexpr operator const char*() const noexcept
{
return data();
}
using iterator = typename IterPolicy::iterator;
using reverse_iterator = typename IterPolicy::reverse_iterator;
using const_iterator = typename IterPolicy::const_iterator;
using const_reverse_iterator = typename IterPolicy::const_reverse_iterator;
constexpr iterator begin() const noexcept { return{ *this, 0 }; }
constexpr const_iterator cbegin() const noexcept { return begin(); }
constexpr iterator end() const noexcept { return{ *this, size() }; }
constexpr const_iterator cend() const noexcept { return end(); }
constexpr reverse_iterator rbegin() const noexcept { return end(); }
constexpr const_reverse_iterator crbegin() const noexcept { return end(); }
constexpr reverse_iterator rend() const noexcept { return begin(); }
constexpr const_reverse_iterator crend() const noexcept { return begin(); }
friend constexpr bool equalAux(const size_t size, const ConstString lhs, const ConstString rhs)
{
return size != 0 ? ((lhs[size - 1] == rhs[size - 1] && equalAux(size - 1, lhs, rhs))) : true;
}
private:
size_t size_;
pointer cstr_;
};
/*class ConstStringSpan : ConstString
{
constexpr ConstStringSpan(ConstString str, iterator first, iterator last) noexcept
: ConstString(str)
{
// assert that last - first >= 0
}
constexpr ConstStringSpan(ConstString str, size_t firstIndex, size_t lastIndex) noexcept
: ConstString(str),
first_(str.begin() + firstIndex),
last_(str.begin() + lastIndex)
{
// assert that lastIndex - firstIndex > 0 and lastIndex < size
}
constexpr size_t size() const noexcept
{
return last_ - first_;
}
constexpr
private:
iterator first_;
iterator last_;
};*/
template<size_t Tsize>
class StaticString
{
using IterPolicy = ArrayIteratorPolicy<StaticString<Tsize>>;
public:
using value_type = const char;
using reference = const char&;
using pointer = const char*;
public:
// Serve for the sole purpose of begin able to be literal type even with default constructor
constexpr StaticString() : cstr_("")
{}
template<size_t otherSize>
constexpr StaticString(const char(&cstr)[otherSize]) : cstr_(cstr)
{}
constexpr char operator[](size_t index) const
{
return (index < Tsize ? cstr_[index] : throw std::out_of_range("Attempt to access a non-existing index of a constant string"));
}
constexpr operator const char*() const noexcept
{
return cstr_;
}
template<size_t TSize1, size_t TSize2>
friend constexpr bool operator==(StaticString<TSize1>& lhs, StaticString<TSize2> rhs)
{
return TSize1 != TSize2 ? false : equalAux(TSize1, lhs, rhs);
}
/* Alternative definition, working on GCC 5.2.0 and Clang 3.6
constexpr bool operator==(StaticString<Tsize> rhs) const
{
for(uint8_t i = 0; i < size(); ++i)
{
if((*this)[i] != rhs[i])
{
return false;
}
}
return true;
}
*/
template<size_t otherSize>
constexpr bool operator==(StaticString<otherSize>) const
{
return false;
}
constexpr size_t size() const noexcept { return Tsize; }
using iterator = typename IterPolicy::iterator;
using reverse_iterator = typename IterPolicy::reverse_iterator;
using const_iterator = typename IterPolicy::const_iterator;
using const_reverse_iterator = typename IterPolicy::const_reverse_iterator;
constexpr iterator begin() const noexcept { return{ *this, 0 }; }
constexpr const_iterator cbegin() const noexcept { return begin(); }
constexpr iterator end() const noexcept { return{ *this, size() }; }
constexpr const_iterator cend() const noexcept { return end(); }
constexpr reverse_iterator rbegin() const noexcept { return end(); }
constexpr const_reverse_iterator crbegin() const noexcept { return end(); }
constexpr reverse_iterator rend() const noexcept { return begin(); }
constexpr const_reverse_iterator crend() const noexcept { return begin(); }
template<size_t TSize1, size_t TSize2>
friend constexpr bool equalAux(const size_t size, const StaticString<TSize1> lhs, const StaticString<TSize2> rhs)
{
return TSize1 != 0 ? ((lhs[TSize1 - 1] == rhs[TSize1 - 1] && equalAux(size - 1, lhs, rhs))) : true;
}
private:
pointer cstr_;
};
namespace CTTI
{
template<class T>
static const char* GetTypeName()
{
static constexpr size_t typeNameLength = sizeof(FUNCTION) - (exprBegin.size() + exprEnd.size());
static constexpr ConstString functionName = FUNCTION;
static StackString<typeNameLength + 1> typeName(functionName.data() + exprBegin.size(), typeNameLength);
return typeName;
}
};
// This file is part of DAWN ( Do Anything With Nothing ) engine.
#ifndef CORE_PLATFORM
#define CORE_PLATFORM
/**@todo Remove useless lines, check if warning should be error, or remove the "try to compile" policy
@todo Test it on lot of platform in order to be sure all works as expected.
*/
///@note Early version, do not rely on it too much !
#define GCC_COMPILER 1
#define MVSC_COMPILER 2
#define ICC_COMPILER 3
#define BORLAND_COMPILER 4
#define LINUX 1
#define WINDOWS 2
#define MAC 3
#define UNKOWN "unknown"
#if defined( __STDC__ ) && !defined( __cplusplus )
# error "This project require a C++ compiler !"
#endif
#if defined( __cplusplus ) && ( __cplusplus == 199711L )
# error "This project require a C++11 compliant compiler in order to build properly!\
If your compiler support C++11, please turn C++11 support option on !"
#endif
#if defined( __GNUC__ ) || defined( __MINGW__ ) || defined ( __clang__ )
# define FUNCTION __PRETTY_FUNCTION__
# define restrict __restrict__
# define force_inline __attribute__((always_inline))
# define likely(x) __builtin_expect((x),1)
# define unlikely(x) __builtin_expect((x),0)
# if defined( __MINGW__ )
# define COMPILER_NAME "MinGW"
# else
# define COMPILER_NAME "gcc"
# endif
# define COMPILER GCC_COMPILER
# if defined( __GNUC_PATCHLEVEL__ )
# define COMPILER_VERSION ( __GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__ )
# else
# define COMPILER_VERSION ( __GNUC__ * 10000 \
+ __GNUC__ * 100 )
# endif
# if( COMPILER_VERSION < 40500 )
# define DEPRECATED(msg) __attribute__((deprecated))
# else
# define DEPRECATED(msg) __attribute__((deprecated(msg)))
# endif
# if(( COMPILER_VERSION >= 40700 ) || defined(__clang__))
# define override override
# define final final
# endif
# define naked __attribute__((naked))
#elif defined( _MSC_VER )
# define FUNCTION __FUNCSIG__
# define restrict __declspec(restrict)
# define force_inline __forceinline
# define likely(x)
# define unlikely(x)
# define COMPILE_NAME "Microsoft compiler"
# define COMPILER MVSC_COMPILER
# if( ( _MSC_VER <= 1200 ) )
# define COMPILER_VERSION _MSC_VER
# else
# define COMPILER_VERSION _MSC_FULL_VER
# endif
# if( COMPILER_VERSION >= 1400 )
# define override override
# define final sealed
# endif
# define naked __declspec(naked)
# define DEPRECATED(msg) __delcspec( deprecated(msg) )
#elif defined( __INTEL_COMPILER ) || defined( __ICL )
# define FUNCTION __PRETTY_FUNCTION__
# define restrict restrict
# define force_inline // Could be a way, but I don't know it yet
# define likely(x) // No support atm
# define unlikely(x)
# define COMPILER_NAME "Intel compiler"
# define COMPILER ICC_COMPILER
# define COMPILER_VERSION __INTEL_COMPILER
# if( COMPILER_VERSION >= 20130000 )
# define override override
# define final final
# else
# define override
# define final
# endif
# define DEPRECATED(msg) __attribute__( ( deprecated ) )
# define naked __attribute__ ((naked ))
#else
# warning "Unknown compiler, there might be some troubles during compilation, and some special features are disabled. \
This may lead to performance problems and/or debugging troubles! Please check list of supported compiler."
# ifdef( __func__ ) // standard since C++ 11 but compiler may not support it
# define FUNCTION __func__
# else
# warning "Compiler is not AINSI C99 standard, can't use __func__. Some debug functions will be disabled"
# endif
# define restrict
# define always_inline
# define COMPILER_NAME UNKNWON
# define COMPILER UNKNOWN
# define COMPILER_VERSION UNKNOWN
# warning "Compiler not recongnized, results may be unexpected"
# define DEPRECATED(t)
#endif
#if (!defined override && !defined final)
# define override
# define final
# warning "Keyword override and final are not defined by your compiler. This may lead to wrong result when using API. Please change your compiler to a supported compiler !"
#endif
#if defined( __gnu_linux__ ) || defined( __linux__ )
# define OS_NAME "Linux"
# define OS LINUX
#elif defined( macintosh ) || defined( Macintosh ) || ( defined( __APPLE__ ) && defined( __MACH__ ) )
# define OS_NAME "Mac"
# define OS MAC
#elif defined( _WIN32 )
# define OS_NAME "Windows"
# define OS WINDOWS
#else
# define OS_NAME UNKNOWN
# define OS UNKNOWN
# warning "Operating system not recognized. Unexpected results might happen"
#endif
#if( OS == LINUX || OS == MAC ) && \
COMPILER_VERSION >= 4000 && \
( COMPILER == ICC_COMPILER || COMPILER == GCC_COMPILER )
# define EXPORT __attribute__( ( visibility( "default" ) ) )
# define IMPORT __attribute__( ( visibility( "default" ) ) )
# define LOCAL __attribute__( ( visibility( "hidden" ) ) )
#elif( OS == WINDOWS )
# define EXPORT __declspec( dllexport )
# define IMPORT __declspec( dllimport )
# define LOCAL
#else
# define EXPORT
# define IMPORT
# define LOCAL
#endif
#if !defined( BUILD_STATIC ) && !defined( IMPORT_LIBRARY )
# define API EXPORT
#elif defined( IMPORT_LIBRARY )
# define API IMPORT
#else
# define API
#endif
#if defined( __amd64__ ) || defined( __x86_64__ ) || defined( _M_X64 ) || defined( _M_AMD64) || \
defined( _ia64__ ) || defined( _M_IA64 ) || defined( __itanium__ )
# define PLATFORM_X64
#else
# define PLATFORM_X86
#endif
#if defined(_DEBUG) || !defined(NDEBUG)
# define DEBUG true
#else
# define DEBUG false
#endif
#endif // CORE_PLATFORM
#ifndef STACK_STRING_HXX
#define STACK_STRING_HXX
#include <algorithm>
#include <cstring>
#include <utility>
//#include "span.h"
// A holder for a string, safer than the raw "const char*", at least for construction
// As the underlying type is a std::array, the string is not movable, and generally allocated on the stack.
// In other words, it's nice when you need small strings in general, that could be copied better than taken by reference
// or moved.
// CAVEHEAT : You can't use operator= for assignement, because of the way they work, the assignement operators in
// the class are not copy operator (copy operator must be non-template functions)
// From the standard :
// §12.8/9:
// A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with
// exactly one parameter of type X, X&, const X&, volatile X& or const volatile X&.
//template<class ... Containers>
constexpr size_t strlength(const char* cstr)
{
size_t size = 0;
for(char c = cstr[0]; c != '\0'; ++c, ++size);
return size;
}
template<size_t size,
bool safeFlag = false>
class StackString final
{
using storage_type = std::array<char, size + 1>;
public:
StackString() = default;
// Not correct !
template<size_t otherSize>
constexpr explicit StackString(const StackString<otherSize>& sstr) noexcept : StackString(sstr, std::integral_constant<bool, (otherSize <= size)>{})
{}
template<size_t otherSize>
constexpr StackString(const char(&cstr)[otherSize]) : str_{buildStr(cstr, std::make_index_sequence<otherSize>{})}
{}
template<size_t otherSize>
constexpr StackString(const char* cstr, std::integral_constant<size_t, otherSize>) : str_{build_str(cstr, std::make_index_sequence<otherSize>{})}
{}
StackString(const char* cstr, size_t n)
{
std::copy_n(cstr, n, str_.data());
}
template<class InputIterator>
StackString(InputIterator first, InputIterator last)
{
auto it = begin();
for(auto elem : itSpan)
{
*it = elem;
++it;
}
}
// Removed the GSL (Guideline Support Library) dependency for this example
/*explicit StackString(gsl::span<char> itSpan)
{
auto it = begin();
for(auto elem : itSpan)
{
*it = elem;
++it;
}
}*/
explicit StackString(const std::string& str) : str_{}
{
copyStr(str);
}
template<size_t otherSize>
constexpr typename std::enable_if<(otherSize <= size), StackString>::type& operator=(const StackString<otherSize>& sstr) noexcept
{
std::strcpy(str_.data(), sstr.data());
return *this;
}
template<size_t otherSize>
constexpr typename std::enable_if<(otherSize > size)>::type operator=(const StackString<otherSize>&) noexcept
{
static_assert(otherSize <= size, "Can't initialize StackString with a string bigger than it's size");
}
StackString& operator=(const char* cstr)
{
copyStr(cstr);
return *this;
}
StackString& operator=(const std::string& str)
{
copyStr(str);
return *this;
}
char* operator[](size_t index) const noexcept(!safeFlag)
{
return bracketAccess(index, std::integral_constant<bool, safeFlag>{});
}
void clear() noexcept
{
str_ = {};
}
const char* data() const noexcept
{
return str_.data();
}
operator const char*() const noexcept
{
return data();
}
auto begin() noexcept { return str_.begin(); }
auto cbegin() const noexcept { return str_.cbegin(); }
auto rbegin() noexcept { return str_.rbegin(); }
auto crbegin() const noexcept { return str_.crbegin(); }
auto end() noexcept { return str_.end(); }
auto cend() const noexcept { return str_.cend(); }
auto rend() noexcept { return str_.rend(); }
auto crend() const noexcept { return str_.crend(); }
private:
template<size_t otherSize>
explicit StackString(const StackString<otherSize>& sstr, std::true_type) noexcept : str_{}
{
std::strcpy(str_.data(), sstr);
}
template<size_t otherSize>
explicit StackString(const StackString<otherSize>&, std::false_type) noexcept
{
static_assert(otherSize <= size, "Can't initialize StackString with a string bigger than it's size");
}
template<size_t otherSize,
size_t stringSize = size,
size_t ... Indices>
constexpr std::enable_if_t<(otherSize <= stringSize), storage_type> buildStr(const char (&cstr)[otherSize], std::index_sequence<Indices...>) noexcept
{
return {{cstr[Indices]...}};
}
template<size_t otherSize,
size_t stringSize = size,
size_t ... Indices>
constexpr std::enable_if_t<(otherSize > stringSize), storage_type> buildStr(const char (&)[otherSize], std::index_sequence<Indices...>) noexcept
{
static_assert(otherSize <= size, "Can't initialize StackString with a string bigger than it's size");
return {};
}
/*template<class InputIterator, size_t ... Indices>
constexpr sotrage_type build_str_from_iterators(const InputIterator first, std::index_sequence<Indices>)
{
return {{(*(first + Indices))...}};
}*/
void copyStr(const std::string& str)
{
if(str.length() <= size)
{
std::strcpy(str_.data(), str.data());
}
else
{
throw std::out_of_range("Attempt to construct StackString with a string bigger than allowed by the type !");
}
}
void copyStr(const char* cstr)
{
if(strlen(cstr) <= size)
{
std::strcpy(str_.data(), cstr);
}
else
{
throw std::out_of_range("Attempt to construct StackString with a string bigger than allowed by the type !");
}
}
char* bracketAccess(size_t index, std::true_type)
{
if(!(index >= size))
{
return str_ + index;
}
throw std::out_of_range("Invalid access of StackString");
}
char* bracketAccess(size_t index, std::false_type) noexcept
{
return str_ + index;
}
private:
storage_type str_;
};
#endif // STACK_STRING_HXX
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment