C++ 实现Variant类

Variant类借鉴于Qt的QVariant类,类似于Boost的any类。它把常用类型使用一个类包装起来,这样使用QVector等容器时,其内部就可以存储不同的数据。例如:

std::vector vec;
vec.push_back(1);        // 整形
vec.push_back("str");    // 字符串
vec.push_back(false);    // bool变量
vec.push_back(1.2);      // 浮点数

当然,没有做到像QVariant类那么全面,现阶段仅仅提供了基本数据类型和std::string、std::wstring、char *, wchar_t *类型的数据封装。后续若有完善,再另行给出源码,目前版本仅仅为大家提供一个参考,与大家共同学习与交流。话不多说,上源码:

(编译器:VS2017 msvc15)

/**
 * \filename	Variant.h
 * \breif		The header file of class Variant.
 * \date		2019-07-21
 * \author		shaoguang
 */

#ifndef VARIANT_H
#define VARIANT_H

#include 

class Variant
{
public:
	using uchar = unsigned char;
	using ushort = unsigned short;
	using uint = unsigned int;
	using ulong = unsigned long;
	using ulonglong = unsigned long long;
	using wchar = wchar_t;
public:
	enum Type {
		Invalid,
		Bool,
		Char,
		UChar,
		Short,
		UShort,
		Int,
		UInt,
		Long,
		ULong,
		LongLong,
		ULongLong,
		Float,
		Double,
		LongDouble,
		WChar,

		String,			// std::string
		WString,		// std::wstring
	};

	template
	static Type typeID();
public:
	Variant();
	~Variant();

	Variant(bool b);
	Variant(char c);
	Variant(uchar uc);
	Variant(wchar_t wc);
	Variant(short s);
	Variant(ushort us);
	Variant(int i);
	Variant(uint ui);
	Variant(long l);
	Variant(ulong ul);
	Variant(long long ll);
	Variant(ulonglong ull);
	Variant(float f);
	Variant(double d);
	Variant(long double ld);
	Variant(const char *str);
	Variant(const wchar_t *wstr);
	Variant(const std::string &str);
	Variant(const std::wstring &wstr);

	Variant(const Variant &other);
	Variant(Variant &&other);
	Variant &operator=(const Variant &other);
	Variant &operator=(Variant &&other);

	Type type() const;
	bool canConvert(Type type) const;
	bool isValid() const;

	bool toBool() const;
	char toChar() const;
	uchar toUchar() const;
	wchar toWChar() const;
	short toShort() const;
	ushort toUShort() const;
	int toInt() const;
	uint toUInt() const;
	long toLong() const;
	ulong toULong() const;
	long long toLongLong() const;
	ulonglong toULongLong() const;
	float toFloat() const;
	double toDouble() const;
	long double toLongDouble() const;
	std::string toString() const;
	std::wstring toWString() const;


private:
	void make_invalid();

	template
	static T strToNumber(const std::string &str);
	template
	static T strToNumber(const std::wstring &wstr);

	template
	T numVariantToHelper(const T &val) const;

	template 
	static inline void safe_delete_void_ptr(void *&target);

private:
	struct Private {
		inline Private() noexcept : type(Invalid), is_shared(false), is_null(true)
		{
			data.ptr = nullptr;
		}

		// Internal constructor for initialized variants.
		explicit inline Private(Type variantType) noexcept
			: type(variantType), is_shared(false), is_null(false)
		{}

		inline Private(const Private &other) noexcept
			: data(other.data), type(other.type),
			is_shared(other.is_shared), is_null(other.is_null)
		{}

		union Data {
			bool b;
			char c;
			uchar uc;
			wchar_t wc;
			short s;
			ushort us;
			int i;
			uint ui;
			long l;
			ulong ul;
			long long ll;
			ulonglong ull;
			float f;
			double d;
			long double ld;
			void *ptr;
		}data;

		Type type;
		bool is_shared;
		bool is_null;
	};
	Private		_d;
};

#endif	// VARIANT_H

 

#include "Variant.h"

#include 

Variant::Variant()
	: _d(Invalid)
{
}

Variant::~Variant()
{
	if (String == _d.type)
	{
		if (_d.data.ptr)
			safe_delete_void_ptr(_d.data.ptr);
	}
	else if (WString == _d.type)
	{
		if (_d.data.ptr)
			safe_delete_void_ptr(_d.data.ptr);
	}
}

Variant::Variant(bool b)
	: _d(Bool)
{
	_d.data.b = b;
}

Variant::Variant(char c)
	: _d(Char)
{
	_d.data.c = c;
}

Variant::Variant(uchar uc)
	: _d(UChar)
{
	_d.data.uc = uc;
}

Variant::Variant(wchar_t wc)
	: _d(WChar)
{
	_d.data.wc = wc;
}

Variant::Variant(short s)
	: _d(Short)
{
	_d.data.s = s;
}
Variant::Variant(ushort us)
	: _d(UShort)
{
	_d.data.us = us;
}
Variant::Variant(int i)
	: _d(Int)
{
	_d.data.i = i;
}
Variant::Variant(uint ui)
	: _d(UInt)
{
	_d.data.ui = ui;
}
Variant::Variant(long l)
	: _d(Long)
{
	_d.data.l = l;
}
Variant::Variant(ulong ul)
	: _d(ULong)
{
	_d.data.ul = ul;
}
Variant::Variant(long long ll)
	: _d(LongLong)
{
	_d.data.ll = ll;
}
Variant::Variant(ulonglong ull)
	: _d(ULongLong)
{
	_d.data.ull = ull;
}
Variant::Variant(float f)
	: _d(Float)
{
	_d.data.f = f;
}
Variant::Variant(double d)
	: _d(Double)
{
	_d.data.d = d;
}
Variant::Variant(long double ld)
	: _d(LongDouble)
{
	_d.data.ld = ld;
}

Variant::Variant(const char *str)
	: _d(String)
{
	if (!str) {
		make_invalid();
	} else {
		size_t len = strlen(str);
		_d.data.ptr = new char[strlen(str) + 1];
		strcpy_s(static_cast(_d.data.ptr), strlen(str) + 1, str);
	}
}

Variant::Variant(const wchar_t * wstr)
{
	if (!wstr) {
		make_invalid();
	} else {
		_d.data.ptr = new char[wcslen(wstr) + 1];
		wcscpy_s((wchar_t *)_d.data.ptr, wcslen(wstr) + 1, wstr);
	}
}

Variant::Variant(const std::string &str)
	: _d(String)
{
	if (str.empty()) {
		make_invalid();
	} else {
		_d.data.ptr = new char[str.size() + 1];
		strcpy_s((char *)_d.data.ptr, str.size() + 1, str.c_str());
	}
}

Variant::Variant(const std::wstring &wstr)
	: _d(WString)
{
	if (wstr.empty()) {
		make_invalid();
	} else {
		_d.data.ptr = new wchar_t[wstr.size() + 1];
		wcscpy_s((wchar_t *)_d.data.ptr, wstr.size() + 1, wstr.c_str());
	}
}

Variant::Variant(const Variant &other)
	: _d(other._d)
{
	if (String == _d.type)
	{
		_d.data.ptr = new char[strlen(static_cast(other._d.data.ptr)) + 1];
		strcpy_s(static_cast(_d.data.ptr), strlen(static_cast(other._d.data.ptr)) + 1, 
			static_cast(other._d.data.ptr));
	}
	else if (WString == _d.type)
	{
		_d.data.ptr = new char[wcslen(static_cast(other._d.data.ptr)) + 1];
		wcscpy_s(static_cast(_d.data.ptr), wcslen(static_cast(other._d.data.ptr)) + 1,
			static_cast(other._d.data.ptr));
	}
}
Variant::Variant(Variant &&other)
	: _d(other._d)
{
	if (String == other._d.type || WString == other._d.type)
	{
		this->_d.data.ptr = other._d.data.ptr;
		other.make_invalid();
	}
}

Variant &Variant::operator=(const Variant &other)
{
	if (this == &other)
		return *this;
	if (String == _d.type || WString == _d.type)
	{
		if (_d.data.ptr)
		{
			delete []_d.data.ptr;
			_d.data.ptr = nullptr;
		}
	}
	if (Invalid == other._d.type)
	{
		make_invalid();
		return *this;
	}
	else if (String == other._d.type)
	{
		_d.data.ptr = new char[strlen(static_cast(other._d.data.ptr)) + 1];
		strcpy_s(static_cast(_d.data.ptr), strlen(static_cast(other._d.data.ptr)) + 1,
			static_cast(other._d.data.ptr));
	}
	else if (WString == other._d.type)
	{
		_d.data.ptr = new char[wcslen(static_cast(other._d.data.ptr)) + 1];
		wcscpy_s(static_cast(_d.data.ptr), wcslen(static_cast(other._d.data.ptr)) + 1, 
			static_cast(other._d.data.ptr));
	}
	else
		_d.data = other._d.data;
	this->_d.type = other._d.type;
	this->_d.is_null = other._d.is_null;
	this->_d.is_shared = other._d.is_shared;
	return *this;
}
Variant &Variant::operator=(Variant &&other)
{
	if (this == &other)
		return *this;
	this->_d = other._d;
	if (String == _d.type || WString == _d.type)
	{
		this->_d.data.ptr = other._d.data.ptr;
		other.make_invalid();
	}
	return *this;
}

Variant::Type Variant::type() const
{
	return _d.type;
}

bool Variant::canConvert(Type type) const
{
	if (Invalid == type)
		return false;
	switch (_d.type)
	{
	case Invalid:		return false;
	case Char:			return WChar != type && WString != type;
	case UChar:			return WChar != type && WString != type;
	case WChar:			return Char != type && String != type;
	case Short:			return true;
	case UShort:		return true;
	case Int:			return true;
	case UInt:			return true;
	case Long:			return true;
	case ULong:			return true;
	case LongLong:		return true;
	case ULongLong:		return true;
	case Float:			return Char != type && UChar != type && WChar != type;
	case Double:		return Char != type && UChar != type && WChar != type;
	case LongDouble:	return Char != type && UChar != type && WChar != type;
	case String:		return WString != type;
	case WString:		return String != type;
	default: break;
	}
	return false;
}

bool Variant::isValid() const
{
	return _d.type != Invalid;
}

std::string Variant::toString() const
{
	if (!isValid() || WString == _d.type)
		return "";
	if (String == _d.type)
		return static_cast(_d.data.ptr);

	std::stringstream ss;
	try {
		switch (_d.type)
		{
		case Bool:
			if (_d.data.b) ss << "true";
			else ss << "false"; break;
		case Char:			ss << _d.data.c;	break;
		case UChar:			ss << _d.data.uc;	break;
		case WChar:								break;
		case Short:			ss << _d.data.s;	break;
		case UShort:		ss << _d.data.us;	break;
		case Int:			ss << _d.data.i;	break;
		case UInt:			ss << _d.data.ui;	break;
		case Long:			ss << _d.data.l;	break;
		case ULong:			ss << _d.data.ul;	break;
		case LongLong:		ss << _d.data.ll;	break;
		case ULongLong:		ss << _d.data.ull;	break;
		case Float:			ss << _d.data.f;	break;
		case Double:		ss << _d.data.d;	break;
		case LongDouble:	ss << _d.data.ld;	break;
		default: break;
		}
	} catch (...) {
		return ss.str();
	}
	
	return ss.str();
}

std::wstring Variant::toWString() const
{
	if (!isValid() || String == _d.type)
		return L"";
	if (WString == _d.type)
		return static_cast(_d.data.ptr);

	std::wstringstream wss;
	try {
		switch (_d.type)
		{
		case Bool:
			if (_d.data.b) wss << L"true";
			else wss << L"false"; break;
		case Char:			wss << _d.data.c;	break;
		case UChar:			wss << _d.data.uc;	break;
		case WChar:			wss << _d.data.wc;	break;
		case Short:			wss << _d.data.s;	break;
		case UShort:		wss << _d.data.us;	break;
		case Int:			wss << _d.data.i;	break;
		case UInt:			wss << _d.data.ui;	break;
		case Long:			wss << _d.data.l;	break;
		case ULong:			wss << _d.data.ul;	break;
		case LongLong:		wss << _d.data.ll;	break;
		case ULongLong:		wss << _d.data.ull;	break;
		case Float:			wss << _d.data.f;	break;
		case Double:		wss << _d.data.d;	break;
		case LongDouble:	wss << _d.data.ld;	break;
		default: break;
		}
	} catch (...) {
		return wss.str();
	}
	
	return wss.str();
}
bool Variant::toBool() const
{
	switch (_d.type)
	{
	case Bool:	return _d.data.b;
	case String: return (strcmp((char *)_d.data.ptr, "true") == 0);
	case WString: return (wcscmp((wchar_t *)_d.data.ptr, L"true") == 0);
	default: return numVariantToHelper(_d.data.b);
	}
}
char Variant::toChar() const
{
	return numVariantToHelper(_d.data.c);
}
Variant::uchar Variant::toUchar() const
{
	return numVariantToHelper(_d.data.uc);
}
wchar_t Variant::toWChar() const
{
	return numVariantToHelper(_d.data.wc);
}
short Variant::toShort() const
{
	return numVariantToHelper(_d.data.s);
}
Variant::ushort Variant::toUShort() const
{
	return numVariantToHelper(_d.data.us);
}
int Variant::toInt() const
{
	return numVariantToHelper(_d.data.i);
}
Variant::uint Variant::toUInt() const
{
	return numVariantToHelper(_d.data.ui);
}
long Variant::toLong() const
{
	return numVariantToHelper(_d.data.l);
}
Variant::ulong Variant::toULong() const
{
	return numVariantToHelper(_d.data.ul);
}
long long Variant::toLongLong() const
{
	return numVariantToHelper(_d.data.ll);
}
Variant::ulonglong Variant::toULongLong() const
{
	return numVariantToHelper(_d.data.ull);
}
float Variant::toFloat() const
{
	return numVariantToHelper(_d.data.f);
}
double Variant::toDouble() const
{
	return numVariantToHelper(_d.data.d);
}
long double Variant::toLongDouble() const
{
	return numVariantToHelper(_d.data.ld);
}

void Variant::make_invalid()
{
	_d.type = Invalid;
	_d.is_null = true;
	_d.is_shared = false;
	_d.data.ptr = nullptr;
}

template
Variant::Type Variant::typeID()
{
	if (std::is_same_v)
		return Bool;
	if (std::is_same_v)
		return Char;
	if (std::is_same_v)
		return UChar;
	if (std::is_same_v)
		return WChar;
	if (std::is_same_v)
		return Short;
	if (std::is_same_v)
		return UShort;
	if (std::is_same_v)
		return Int;
	if (std::is_same_v)
		return UInt;
	if (std::is_same_v)
		return Long;
	if (std::is_same_v)
		return ULong;
	if (std::is_same_v)
		return LongLong;
	if (std::is_same_v)
		return ULongLong;
	if (std::is_same_v)
		return Float;
	if (std::is_same_v)
		return Double;
	if (std::is_same_v)
		return LongDouble;
	if (std::is_same_v)
		return String;
	if (std::is_same_v)
		return WString;

	return Invalid;
}

template
T Variant::strToNumber(const std::string &str)
{
	try {
		switch (typeID())
		{
		case Bool:			return stoi(str);		break;
		case Char:			return stoi(str);		break;
		case UChar:			return stoul(str);		break;
		case WChar:			return stoi(str);		break;
		case Short:			return stoi(str);		break;
		case UShort:		return stoul(str);		break;
		case Int:			return stoi(str);		break;
		case UInt:			return stoul(str);		break;
		case Long:			return stol(str);		break;
		case ULong:			return stoul(str);		break;
		case LongLong:		return stoll(str);		break;
		case ULongLong:		return stoull(str);;	break;
		case Float:			return stof(str);		break;
		case Double:		return stod(str);		break;
		case LongDouble:	return stold(str);		break;
		default: break;
		}
	} catch (...) {
		return T();
	}
	
	return T();
}

template
T Variant::strToNumber(const std::wstring &wstr)
{
	try {
		switch (typeID())
		{
		case Bool:			return stoi(wstr);		break;
		case Char:			return stoi(wstr);		break;
		case UChar:			return stoul(wstr);		break;
		case WChar:			return stoi(wstr);		break;
		case Short:			return stoi(wstr);		break;
		case UShort:		return stoul(wstr);		break;
		case Int:			return stoi(wstr);		break;
		case UInt:			return stoul(wstr);		break;
		case Long:			return stol(wstr);		break;
		case ULong:			return stoul(wstr);		break;
		case LongLong:		return stoll(wstr);		break;
		case ULongLong:		return stoull(wstr);;	break;
		case Float:			return stof(wstr);		break;
		case Double:		return stod(wstr);		break;
		case LongDouble:	return stold(wstr);		break;
		default: break;
		}
	} catch (const std::exception&) {
		return T();
	}
	
	return T();
}

template
T Variant::numVariantToHelper(const T &val) const
{
	if (typeID() == _d.type)
		return val;

	switch (_d.type)
	{
	case Bool:			return T(_d.data.b);	break;
	case Char:			return T(_d.data.c);	break;
	case UChar:			return T(_d.data.uc);	break;
	case WChar:			return T(_d.data.wc);	break;
	case Short:			return T(_d.data.s);	break;
	case UShort:		return T(_d.data.us);	break;
	case Int:			return T(_d.data.i);	break;
	case UInt:			return T(_d.data.ui);	break;
	case Long:			return T(_d.data.l);	break;
	case ULong:			return T(_d.data.ui);	break;
	case LongLong:		return T(_d.data.ll);	break;
	case ULongLong:		return T(_d.data.ull);;	break;
	case Float:			return T(_d.data.f);	break;
	case Double:		return T(_d.data.d);	break;
	case LongDouble:	return T(_d.data.ld);	break;
	case String:		return strToNumber(static_cast(_d.data.ptr));
	case WString:		return strToNumber(static_cast(_d.data.ptr));
	default: break;
	}
	return T();
}

template
inline void Variant::safe_delete_void_ptr(void *&target)
{
	if (target)
	{
		T temp = static_cast(target);
		delete []temp;
		temp = nullptr;
		target = nullptr;
	}
}

 

 

 下面写一个简单的测试程序:

#include 
#include 
using namespace std;

#include "Variant.h"
#include 

int main(int argc, char *argv[])
{
	std::vector vec;
	vec = {true, 'c', 1, 1.2, "shaoguang"};

	for (auto &v : vec)
		std::cout << v.toString() << std::endl;

	system("pause");
	return 0;
}

输出结果:

C++ 实现Variant类_第1张图片

你可能感兴趣的:(C++应用)