Skip to content

Instantly share code, notes, and snippets.

@graphitemaster
Last active December 30, 2018 11:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save graphitemaster/2308283 to your computer and use it in GitHub Desktop.
Save graphitemaster/2308283 to your computer and use it in GitHub Desktop.
/*
* GCC and GCC-like compilers set this flag when -std=c++0x is set
* we can use these to make this code use some C++11 features.
*/
#ifdef __GXX_EXPERIMENTAL_CXX0X__
#define SASSERT(X,Y) static_assert(X,Y)
#define NULLPTR nullptr
#else
/*
* Primitive static assert implementation:
*
* Expression fails, char is typedef'ed with
* a negitive value, which is illegal and will
* trigger a compiler error.
*/
#define SASSERT(X,Y) \
typedef char static_assert_ ## __LINE__ [(X)?1:-1]
/*
* We'll implement out own nullptr
*/
const class
{
public:
/*
* Convertible to any type of non-null member
* pointers.
*/
template<typename T>
inline operator T*() const { return 0; }
/*
* Also convertible to any type of null member
* pointers.
*/
template<typename T1, typename T2>
inline operator T1 T2::*() const { return 0;}
private:
/*
* This will make it impossible to take the
* address of nullptr
*/
void operator&() const;
} NULLPTR = {};
#endif
struct holding_failure {};
struct holding_empty {};
struct holding_policy
{
typedef void base_type;
typedef base_type* value_type;
typedef size_t size_type;
virtual value_type get (value_type*) = 0;
virtual void destroy(value_type*) = 0;
virtual void copy (void const*, value_type*) = 0;
virtual void clone (value_type const*, value_type*) = 0;
virtual void move (value_type const*, value_type*) = 0;
virtual size_type size () = 0;
};
template<typename T>
struct holding_policy_base : holding_policy {
virtual size_t size() { return sizeof(T); }
};
/*
* Minimal holding policy does not require any sort
* of managment of class constructors or destructors
* The reason is `holding_policy_min` is only required
* for POD ( plain old data types ). Alternitive types
* specifically user-defined types, or standard library
* types use the latter policy, dubbed, holding_policy_max.
*/
template<typename T>
struct holding_policy_min : holding_policy_base<T>
{
virtual void destroy(
holding_policy::value_type* x
){/* POD types have no dtor */}
virtual void copy (
holding_policy::base_type const* src,
holding_policy::value_type* dst
){
// placement new
new(dst) T(*reinterpret_cast<T const*>(src));
}
virtual void clone(
holding_policy::value_type const* src,
holding_policy::value_type* dst
){ *dst = *src; }
virtual void move(
holding_policy::value_type const* src,
holding_policy::value_type* dst
){ *dst = *src; }
virtual holding_policy::base_type* get(holding_policy::value_type* src){
return reinterpret_cast<holding_policy::value_type>(src);
}
};
/*
* This is the policy structure for larger types,
* or rather, non POD types, such as user defined
* types, or standard library containers. This
* handles proper construction, and destruction,
* allowing safe use of holding.
*/
template<typename T>
struct holding_policy_max : holding_policy_base<T>
{
virtual void destroy(
holding_policy::value_type* x
){
if (*x)
{
/*
* Proper class destruction
*/
delete (*reinterpret_cast<T**>(x));
*x = NULLPTR;
}
}
virtual void copy (
holding_policy::base_type const* src,
holding_policy::value_type* dst
){
*dst = new T(*reinterpret_cast<T const*>(src));
}
virtual void clone(
holding_policy::value_type const* src,
holding_policy::value_type* dst
){
*dst = new T(**reinterpret_cast<T* const*>(src));
}
virtual void move(
holding_policy::value_type const* src,
holding_policy::value_type* dst
) {
/*
* Proper class destruction, and reconstruction
* for movement of data.
*/
(*reinterpret_cast<T**>(dst))->~T();
**reinterpret_cast<T**>(dst) = **reinterpret_cast<T* const*>(src);
}
virtual holding_policy::base_type* get(holding_policy::value_type* src){
return *src;
}
};
/*
* Class fowarding of holding_type, since policy selection
* requires this beforehand as a template specialization.
*/
struct holding_type;
/*
* Policy selectors, this will allow policy selection via type def,
* 100% compile-time translation.
*/
template<typename T>
struct holding_policy_select { typedef holding_policy_max<T> type; };
template<typename T>
struct holding_policy_select<T*> { typedef holding_policy_min<T*> type; };
template<>
struct holding_policy_select<holding_type> {
typedef void type;
};
/*
* Template specializations of POD types, for min holding policys
* these must be modified if C++ decides to add any more language
* specific types.
*/
template<> struct holding_policy_select<signed char> {typedef holding_policy_min<signed char> type;};
template<> struct holding_policy_select<unsigned char> {typedef holding_policy_min<unsigned char> type;};
template<> struct holding_policy_select<signed short> {typedef holding_policy_min<signed short> type;};
template<> struct holding_policy_select<unsigned short>{typedef holding_policy_min<unsigned short> type;};
template<> struct holding_policy_select<signed int> {typedef holding_policy_min<signed int> type;};
template<> struct holding_policy_select<unsigned int> {typedef holding_policy_min<unsigned int> type;};
template<> struct holding_policy_select<signed long> {typedef holding_policy_min<signed long> type;};
template<> struct holding_policy_select<unsigned long> {typedef holding_policy_min<unsigned long> type;};
template<> struct holding_policy_select<float> {typedef holding_policy_min<float> type;};
template<> struct holding_policy_select<bool> {typedef holding_policy_min<bool> type;};
/*
* This function will return the correct `empty` type for any type
* using the policy system above.
*/
template<typename T>
holding_policy* holding_policy_get()
{
/*
* Store as static for compile time optimizations.
*/
static typename holding_policy_select<T>::type p;
return &p;
}
/*
* We need a way to validate that the user specifies
* const char * opposed to char * for getting a string
* literal, otherwise the cast will throw a runtime
* error, and it's better to catch that during compilation
* opposed to runtime. Thats what this is for.
*/
template<typename T> struct holding_is_charptr {enum{value=0};};
template<> struct holding_is_charptr<char*> {enum{value=1};};
/*
* The following class below implements the variant type required
* to create the holding class dubbed `holding_type`
*/
struct holding_type
{
public:
/*
* Initializing constructor
*/
template<typename T>
holding_type(const T& rhs) :
m_policy(holding_policy_get<holding_empty>()),
m_object(NULLPTR) {
assign(rhs);
}
/*
* Empty constructor to prevent the compiler
* from creating a trivial constructor.
*/
holding_type() :
m_policy(holding_policy_get<holding_empty>()),
m_object(NULLPTR)
{}
/*
* Special constructing for string literals only.
* This should be considered UB, but the standard
* says it's safe.
*/
holding_type(const char *c) :
m_policy(holding_policy_get<holding_empty>()),
m_object(NULLPTR) {
assign(c);
}
/*
* Default copy constructor, exactly the same as
* any other constructor, the assign() method
* does all the work. Constructor initializer lists
* are exactly the same straight through.
*/
holding_type(const holding_type& c) :
m_policy(holding_policy_get<holding_empty>()),
m_object(NULLPTR) {
assign(c);
}
/*
* Simple deconstruction, all handled by the policy
* system for POD and user defined types. Safe for
* any object.
*/
~holding_type() {
m_policy->destroy(&m_object);
}
/*
* This function handles all the work for assignment,
* which is used in all the constructors, even the
* copy constructor.
*/
template<typename T>
holding_type& assign(const T& x)
{
/*
* *this needs to be reset for assignment of
* new data, otherwise errors could occur.
*/
reset();
/*
* Get the correct policy, and build from value
* of `x` which is of type `T`
*/
m_policy = holding_policy_get<T>();
m_policy->copy(&x, &m_object);
return *this;
}
/*
* Assignment from another holding_type, overloaded
* function.
*/
holding_type& assign(const holding_type& x)
{
/*
* *this needs to be reset for assignment of
* new data, otherwise errors could occur.
*/
reset();
/*
* Think of this as a copy constructor, since
* the data for *this will be taken from x.
* The only different between this assignment
* and the former is this one does not copy from
* value, but rather clones from the previous
* holding_type, which is better.
*/
m_policy = x.m_policy;
m_policy->clone(&x.m_object, &m_object);
return *this;
}
/*
* Assignment operator, allowing assignment
* of *this like a normal type, the actual work
* is done by the aformentioned assign functions.
*/
template<typename T>
holding_type& operator=(const T& x) { return assign(x);}
/*
* Assignment operator for literal strings, since things like
* const char[x] are not considered const char* by the compiler
* as a operator translation step, but rather are implitically
* convertable to const char * as a translation step.
*/
holding_type& operator=(const char* x) { return assign(x); }
/*
* Reset method, statically deletes the given object
* within the correct policy, while resetting the policy
* of a new holding_empty policy
*/
void reset()
{
m_policy->destroy(&m_object);
m_policy = holding_policy_get<holding_empty>();
}
/*
* Returns true if object contains no
* actual physical value.
*/
bool empty() const {
return m_policy == holding_policy_get<holding_empty>();
}
/*
* Returns true if type types are of the same,
* and thus can be compatible.
*/
bool compat(holding_type& x) const {
return m_policy == x.m_policy;
}
/*
* Utility swap function
*/
holding_type& swap(holding_type& x)
{
/*
* Use internal swap functions
* replace with std::swap() if
* required.
*/
internal_swap(m_policy, x.m_policy);
internal_swap(m_object, x.m_object);
return *this;
}
/*
* This function is what gives back the correct
* type of the data, via cast. This is all possible
* and totally safe thanks to the policy system.
*/
template<typename T>
T& cast()
{
SASSERT(
holding_is_charptr<T>::value != 1,
"error, using holding_type::cast() with non-const `char*`, use `const char*` instead"
);
if (m_policy != holding_policy_get<T>())
throw holding_failure();
T* r = reinterpret_cast<T*>(m_policy->get(&m_object));
return *r;
}
private:
/*
* We implement our own internal swaping
* system for people who choose not to
* use the standard library. Other wise
* if you do so choose, replace the calls
* in the utility swap function above
* with calls to std::swap().
*/
template<typename T>
void internal_swap(T& src, T& dst)
{
T cmp(src);
src = dst;
dst = src;
}
/*
* The actual data is stored here
* everything as a void *, and a policy
* to manage, assign, and create from
* this object safely.
*/
holding_policy* m_policy;
void * m_object;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment