I was attempting to do exactly what you're talking about (see this thread). Like yours, my first inclination was to use a void* but it is all but impossible to do for various reasons. I ended up writing a class that works just lke boost.any, relying solely on templates and a lot of specialization (for singleton variables, [static] arrays, and pointers). I'll post the code here with sample usage for you to review or work from, but keep in mind it must still be considered beta code (I stopped working on it about 4 months ago).
Code:
//
// 06/15/05
// [email protected]
// http://www.geocities.com/lucky760
//
#include <sstream>
#include <algorithm>
#include <iostream>
using namespace std;
class Putty {
class Generic {
public:
virtual const std::type_info& type_info() const = 0;
virtual Generic* copy() const = 0;
virtual std::ostream& output(std::ostream &out) const = 0;
};
//////////////////////////////////////////////////////////////////////////////
template <typename T>
class GenericTemplate : public Generic {
public:
virtual const std::type_info& type_info() const {
return typeid(T);
}
virtual std::ostream& output(std::ostream &out) const = 0;
virtual T* get_ptr() = 0;// should the pointer be const?
};
//////////////////////////////////////////////////////////////////////////////
template <typename value_type>
class GenericSingleton : public GenericTemplate<value_type> {
value_type _value;
public:
GenericSingleton(const value_type &rhs)
: _value(rhs)
{
}
virtual Generic* copy() const {
return new GenericSingleton(_value);
}
virtual std::ostream& output(std::ostream &out) const {
return out << _value;
}
virtual value_type* get_ptr() {
return &_value;
}
};
//////////////////////////////////////////////////////////////////////////////
template <typename ptr_type>
class GenericPointer : public GenericTemplate<ptr_type> {
const ptr_type *_ptr;
public:
GenericPointer(const ptr_type *&rhs)
: _ptr(rhs)
{
}
virtual Generic* copy() const {
return new GenericPointer(_ptr);
}
virtual std::ostream& output(std::ostream &out) const {
if (_ptr == 0)
return out;
return out << _ptr;
}
virtual ptr_type* get_ptr() {
return _ptr;
}
};
//////////////////////////////////////////////////////////////////////////////
template <typename data_type>
class GenericArray : public GenericTemplate<data_type> {
data_type *_data;
size_t _el;
public:
GenericArray(const data_type *rhs, unsigned int elements)
: _el(elements), _data(0)
{
if (_el == 0)
_data = const_cast<data_type*>(rhs);
else {
_data = new data_type[_el];
memcpy(_data, rhs, _el * sizeof(data_type));
}
}
~GenericArray() { if (_data && _el > 0) delete [] _data; }
virtual Generic* copy() const {
return new GenericArray(_data, _el);
}
virtual std::ostream& output(std::ostream &out) const {
if (_data == 0)
return out;
std::string type = type_info().name();
if (type.substr(0, 4) == "char")
return out << _data;
size_t last = _el - 1;
out << "{ ";
for (size_t i = 0; i < _el; ++i) {
out << _data[i];
out << (i < last ? ", " : " ");
}
return out << "}";
}
virtual data_type* get_ptr() {
return _data;
}
};
template <typename T>
static Generic* create(const T &rhs) {
return new GenericSingleton<T>(rhs);
}
template <typename T>
static Generic* create(const T *rhs) {
return new GenericSingleton<const T*>(rhs);
}
template <typename T>
static Generic* create(const T &rhs, unsigned int elements) {
// this only happens for pointers
return 0;//new GenericPointer<T*>(rhs, elements);
}
template <typename T>
static Generic* create(const T *rhs, unsigned int elements) {
// this only happens for arrays
return new GenericArray<T>(rhs, elements);
}
Generic *_content;
public:
Putty() : _content(0) { }
Putty(const Putty &rhs)
: _content(rhs._content ? rhs._content->copy() : 0)
{
}
template <typename T>
Putty(const T &value) {
std::string name = typeid(T).name();
bool ptr = name.find("*") != std::string::npos;
std::string::size_type brack = name.find("[");
bool arr = brack != std::string::npos;
if (ptr && !arr) {
_content = create(value);//, 0);
}
// when T is in the form of "int"
else if (!arr) {
_content = create(value);
}
// when T is in the form of "int* or char[14]"
else {
unsigned int el;
std::istringstream istr(name.substr(brack + 1));
istr >> el;
_content = create(value, el);
}
}
Putty& swap(Putty &rhs) {
std::swap(_content, rhs._content);
return *this;
}
Putty& operator=(const Putty &rhs) {
return swap(Putty(rhs));
}
template <typename T>
Putty& operator=(const T &rhs) {
return swap(Putty(rhs));
}
~Putty() { if (_content) delete _content; }
const std::type_info& type_info() const {
return _content ? _content->type_info() : typeid(void);
}
friend std::ostream& operator<<(std::ostream &out, const Putty &rhs) {
return rhs._content ? rhs._content->output(out) : out;
}
};
int main() {
int a = 110;
int b[] = { 111, 222, 333 };
int *c = b;
char d = 'X';
char e[] = "This is a character array!";
char *f = e;
{
Putty singleton(a);
cout << singleton << endl;
cout << "------------------------" << endl;
Putty array(b);
cout << array << endl;
cout << "------------------------" << endl;
Putty pointer(c);
cout << pointer << endl;
}
cout << "=============================" << endl;
{
Putty singleton(d);
cout << singleton << endl;
cout << "------------------------" << endl;
Putty array(e);
cout << array << endl;
cout << "------------------------" << endl;
Putty pointer(f);
cout << pointer << endl;
}
return 0;
}
Originally Posted by
the output of that program
110
------------------------
{ 111, 222, 333 }
------------------------
0066F828
=============================
X
------------------------
This is a character array!
------------------------
This is a character array!