C++ 实现Variant类

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

std::vector<Variant> 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 <string>

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<typename T>
    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<typename T>
    static T strToNumber(const std::string &str);
    template<typename T>
    static T strToNumber(const std::wstring &wstr);

    template<typename T>
    T numVariantToHelper(const T &val) const;

    template <typename T>
    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 initializing variant.
        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 <sstream>

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

Variant::~Variant()
{
    if (String == _d.type)
    {
        if (_d.data.ptr)
            safe_delete_void_ptr<char *>(_d.data.ptr);
    }
    else if (WString == _d.type)
    {
        if (_d.data.ptr)
            safe_delete_void_ptr<wchar_t *>(_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<char *>(_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<char *>(other._d.data.ptr)) + 1];
        strcpy_s(static_cast<char *>(_d.data.ptr), strlen(static_cast<char *>(other._d.data.ptr)) + 1,
            static_cast<char *>(other._d.data.ptr));
    }
    else if (WString == _d.type)
    {
        _d.data.ptr = new char[wcslen(static_cast<wchar_t *>(other._d.data.ptr)) + 1];
        wcscpy_s(static_cast<wchar_t *>(_d.data.ptr), wcslen(static_cast<wchar_t *>(other._d.data.ptr)) + 1,
            static_cast<wchar_t *>(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<char *>(other._d.data.ptr)) + 1];
        strcpy_s(static_cast<char *>(_d.data.ptr), strlen(static_cast<char *>(other._d.data.ptr)) + 1,
            static_cast<char *>(other._d.data.ptr));
    }
    else if (WString == other._d.type)
    {
        _d.data.ptr = new char[wcslen(static_cast<wchar_t *>(other._d.data.ptr)) + 1];
        wcscpy_s(static_cast<wchar_t *>(_d.data.ptr), wcslen(static_cast<wchar_t *>(other._d.data.ptr)) + 1,
            static_cast<wchar_t *>(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<char *>(_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<wchar_t *>(_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<bool>(_d.data.b);
    }
}
char Variant::toChar() const
{
    return numVariantToHelper<char>(_d.data.c);
}
Variant::uchar Variant::toUchar() const
{
    return numVariantToHelper<uchar>(_d.data.uc);
}
wchar_t Variant::toWChar() const
{
    return numVariantToHelper<wchar_t>(_d.data.wc);
}
short Variant::toShort() const
{
    return numVariantToHelper<short>(_d.data.s);
}
Variant::ushort Variant::toUShort() const
{
    return numVariantToHelper<ushort>(_d.data.us);
}
int Variant::toInt() const
{
    return numVariantToHelper<int>(_d.data.i);
}
Variant::uint Variant::toUInt() const
{
    return numVariantToHelper<uint>(_d.data.ui);
}
long Variant::toLong() const
{
    return numVariantToHelper<long>(_d.data.l);
}
Variant::ulong Variant::toULong() const
{
    return numVariantToHelper<ulong>(_d.data.ul);
}
long long Variant::toLongLong() const
{
    return numVariantToHelper<long long>(_d.data.ll);
}
Variant::ulonglong Variant::toULongLong() const
{
    return numVariantToHelper<ulonglong>(_d.data.ull);
}
float Variant::toFloat() const
{
    return numVariantToHelper<float>(_d.data.f);
}
double Variant::toDouble() const
{
    return numVariantToHelper<double>(_d.data.d);
}
long double Variant::toLongDouble() const
{
    return numVariantToHelper<long double>(_d.data.ld);
}

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

template<typename T>
Variant::Type Variant::typeID()
{
    if (std::is_same_v<T, bool>)
        return Bool;
    if (std::is_same_v<T, char>)
        return Char;
    if (std::is_same_v<T, uchar>)
        return UChar;
    if (std::is_same_v<T, wchar_t>)
        return WChar;
    if (std::is_same_v<T, short>)
        return Short;
    if (std::is_same_v<T, ushort>)
        return UShort;
    if (std::is_same_v<T, int>)
        return Int;
    if (std::is_same_v<T, uint>)
        return UInt;
    if (std::is_same_v<T, long>)
        return Long;
    if (std::is_same_v<T, ulong>)
        return ULong;
    if (std::is_same_v<T, long long>)
        return LongLong;
    if (std::is_same_v<T, ulonglong>)
        return ULongLong;
    if (std::is_same_v<T, float>)
        return Float;
    if (std::is_same_v<T, double>)
        return Double;
    if (std::is_same_v<T, long double>)
        return LongDouble;
    if (std::is_same_v<T, std::string>)
        return String;
    if (std::is_same_v<T, std::wstring>)
        return WString;

    return Invalid;
}

template<typename T>
T Variant::strToNumber(const std::string &str)
{
    try {
        switch (typeID<T>())
        {
        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<typename T>
T Variant::strToNumber(const std::wstring &wstr)
{
    try {
        switch (typeID<T>())
        {
        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<typename T>
T Variant::numVariantToHelper(const T &val) const
{
    if (typeID<T>() == _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<T>(static_cast<char *>(_d.data.ptr));
    case WString:        return strToNumber<T>(static_cast<wchar_t *>(_d.data.ptr));
    default: break;
    }
    return T();
}

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

 

 

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

#include <iostream>
#include <stdlib.h>
using namespace std;

#include "Variant.h"
#include <vector>

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

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

    system("pause");
    return 0;
}

输出结果:

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值