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;
}
输出结果: