Typeless Variables
// Create and initialize 2 int type variables
Value<int> int1(20);
Value<int> int2(30);
// Obtain pointers to the previously created variables
Any* any1 = &int1;
Any* any2 = &int2;
// Assign an int type value to int2 (through any2)
any2->set<int>(50);
// Assign the value of int2 to int1 (through the anonymous Any type)
any1->copy(*any2);
// Create a new Any containing the type and value of any2
Any* any3 = any2->clone();
// Create and initialize a string type variable
Value<std::string> string1("Text");
// Set the value directly through the dereference operator which is part of Value<T>
*string1 = "Testing";
// This will throw an exception due to incompatible types
any1->copy(string1);
Typeless variables have many uses in c++.
In particular they have interesting uses in implementing:
- Game object properties system
- Serializable data for configuration or networking
- Sharing, holding and queueing any data in a threaded environment
This particular implementation uses templates for accessing and setting data.
The
Any class allows the data to be copied or cloned without knowing the underlaying type.
The
Value class is an implementation of
Any and serves as a container for all types that implement
operator= and a
copy constructor.
Type-safety is handled at run-time through the use of exceptions.
The concept can also easily be extending using the factory pattern and a type database to create truly anonymous types at run-time.
For example, I've utilized these anonymous variables to serialize complete classes and trees of data across the network.
On the right side there's a snippet that shows how my implementation of typeless variables is used.
Below you can find the actual implementation of typeless variables (press 'Show' to display the source code).
#ifndef UTILS_Any_H
#define UTILS_Any_H
#include <typeinfo>
#include <string>
namespace Utils
{
class Any
{
friend Any;
public:
virtual ~Any() {}
// Constant during a single run-time, may vary across multiple runs
virtual const std::type_info& type() const = 0;
template <class T> bool isType() const;
// Getter/setter
template <class T> void set(const T& value);
template <class T> const T& get() const;
// Anonymous modifications
void copy(const Any& any);
virtual Any* clone() const = 0;
protected:
Any() {}
virtual void _set(void* value) = 0;
virtual void* _get() const = 0;
private:
// Disable implicit copying
Any(const Any&) {}
Any& operator=(const Any& any) { return *this; }
};
}
#include "Any.inl"
#endif
#include <exception>
namespace Utils
{
template <class T> inline bool Any::isType() const
{
return (this->type() == typeid(T));
}
template <class T> inline void Any::set(const T& value)
{
if (typeid(T) != this->type())
throw std::exception("Type mismatch in Any::set");
_set((void*)&value);
}
template <class T> inline const T& Any::get() const
{
if (typeid(T) != this->type())
throw std::exception("Type mismatch in Any::get");
return *(const T*)this->_get();
}
inline void Any::copy(const Any& any)
{
if (any.type() != this->type())
throw std::exception("Type mismatch in Any::copy");
this->_set(any._get());
}
}
#ifndef UTILS_Value_H
#define UTILS_Value_H
#include "Any.h"
namespace Utils
{
template <class T> class Value : public Any
{
public:
Value() : mValue() {}
Value(T value) : mValue(value) {}
Value(const Any& any) { this->copy(any); }
// Implement Any
virtual const std::type_info& type() const { return typeid(T); }
virtual Any* clone() const;
// Some convenient access methods
inline T* operator->() { return &mValue; }
inline const T* operator->() const { return &mValue; }
inline T& operator*() { return mValue; }
inline const T& operator*() const { return mValue; }
protected:
T mValue;
// Implement any
virtual void _set(void* value);
virtual void* _get() const;
};
}
#include "Value.inl"
#endif
#include <assert.h>
namespace Utils
{
template <class T> inline Any* Value<T>::clone() const
{
return new Value<T>(mValue);
}
template <class T> inline void Value<T>::_set(void* value)
{
// Assumes type safety has been taken care of by Any::set
const T* pointer = (const T*)value;
mValue = *pointer;
}
template <class T> inline void* Value<T>::_get() const
{
// Assumes type safety has been taken care of by Any::get
return (void*)&mValue;
}
}