Program Listing for File variant.hpp

Return to documentation for file (include/hsmcpp/variant.hpp)

// Copyright (C) 2021 Igor Krechetov
// Distributed under MIT license. See file LICENSE for details

#ifndef HSMCPP_VARIANT_HPP
#define HSMCPP_VARIANT_HPP

#include <functional>
#include <list>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>

namespace hsmcpp {

class Variant;

using ByteArray_t = std::vector<unsigned char>;
using VariantVector_t = std::vector<Variant>;
using VariantList_t = std::list<Variant>;
using VariantMap_t = std::map<Variant, Variant>;
using VariantPair_t = std::pair<Variant, Variant>;

// cppcheck-suppress misra-c2012-20.7 ; parentheses are not needed
#define DEF_CONSTRUCTOR(_val_type, _internal_type)               \
 \
  explicit Variant(const _val_type v);

// cppcheck-suppress misra-c2012-20.7 ; parentheses are not needed
#define DEF_OPERATOR_ASSIGN(_val_type, _internal_type)                                                      \
 \
  Variant& operator=(const _val_type v);

#define DEF_MAKE_DOC(_internal_type)                                            \
   \
                   \

// cppcheck-suppress misra-c2012-20.7 ; parentheses are not needed
#define DEF_MAKE(_val_type, _internal_type) \
  DEF_MAKE_DOC(_internal_type)              \
  static Variant make(const _val_type v);

class Variant {
private:
    // using MemoryHandlerFunc_t = std::function<void*(void*, const bool)>;
    using MemoryAllocatorFunc_t = std::function<std::shared_ptr<void>(const void*)>;
    using CompareFunc_t = std::function<int(const void*, const void*)>;

public:
    enum class Type {
        UNKNOWN,

        BYTE_1,
        BYTE_2,
        BYTE_4,
        BYTE_8,

        UBYTE_1,
        UBYTE_2,
        UBYTE_4,
        UBYTE_8,

        DOUBLE,
        BOOL,

        STRING,
        BYTEARRAY,

        LIST,
        VECTOR,

        MAP,
        PAIR,

        CUSTOM
    };

public:
    DEF_MAKE(int8_t, Type::BYTE_1);
    DEF_MAKE(int16_t, Type::BYTE_2);
    DEF_MAKE(int32_t, Type::BYTE_4);
    DEF_MAKE(int64_t, Type::BYTE_8);
    DEF_MAKE(uint8_t, Type::UBYTE_1);
    DEF_MAKE(uint16_t, Type::UBYTE_2);
    DEF_MAKE(uint32_t, Type::UBYTE_4);
    DEF_MAKE(uint64_t, Type::UBYTE_8);
    DEF_MAKE(double, Type::DOUBLE);
    DEF_MAKE(bool, Type::BOOL);
    DEF_MAKE(std::string&, Type::STRING);
    DEF_MAKE(char*, Type::STRING);
    DEF_MAKE(ByteArray_t&, Type::VECTOR);

    static Variant make(const char* binaryData, const size_t bytesCount);

    DEF_MAKE(VariantVector_t&, Type::VECTOR);
    template <typename T>
    DEF_MAKE(std::vector<T>&, Type::VECTOR);
    DEF_MAKE(VariantList_t&, Type::LIST);
    template <typename T>
    DEF_MAKE(std::list<T>&, Type::LIST);
    DEF_MAKE(VariantMap_t&, Type::MAP);

    DEF_MAKE_DOC(Type::MAP)
    template <typename K, typename V>
    static Variant make(const std::map<K, V>& v);

    DEF_MAKE(VariantPair_t&, Type::PAIR);

    template <typename TFirst, typename TSecond>
    static Variant make(const TFirst& first, const TSecond& second);

    DEF_MAKE_DOC(Type::CUSTOM)
    template <typename T>
    static Variant make(const T& v);

    DEF_MAKE_DOC(Type::CUSTOM)
    template <typename T>
    static Variant makeCustom(const T& v);

    static Variant make(const Variant& v);

public:
    Variant() = default;

    Variant(const Variant& v);

    Variant(Variant&& v) noexcept;

    ~Variant();

    DEF_CONSTRUCTOR(int8_t, Type::BYTE_1)
    DEF_CONSTRUCTOR(int16_t, Type::BYTE_2)
    DEF_CONSTRUCTOR(int32_t, Type::BYTE_4)
    DEF_CONSTRUCTOR(int64_t, Type::BYTE_8)
    DEF_CONSTRUCTOR(uint8_t, Type::UBYTE_1)
    DEF_CONSTRUCTOR(uint16_t, Type::UBYTE_2)
    DEF_CONSTRUCTOR(uint32_t, Type::UBYTE_4)
    DEF_CONSTRUCTOR(uint64_t, Type::UBYTE_8)
    DEF_CONSTRUCTOR(double, Type::DOUBLE)
    DEF_CONSTRUCTOR(bool, Type::BOOL)
    DEF_CONSTRUCTOR(std::string&, Type::STRING)
    explicit Variant(const char* v);

    DEF_CONSTRUCTOR(ByteArray_t&, Type::BYTEARRAY)
    explicit Variant(const char* binaryData, const size_t bytesCount);

    DEF_CONSTRUCTOR(VariantVector_t&, Type::VECTOR)
    DEF_CONSTRUCTOR(VariantList_t&, Type::LIST)
    DEF_CONSTRUCTOR(VariantMap_t&, Type::MAP)
    DEF_CONSTRUCTOR(VariantPair_t&, Type::PAIR)

    template <typename T>
    DEF_CONSTRUCTOR(std::vector<T>&, Type::VECTOR);
    template <typename T>
    DEF_CONSTRUCTOR(std::list<T>&, Type::LIST);
    template <typename K, typename V>
    explicit Variant(const std::map<K, V>& v);

    template <typename TFirst, typename TSecond>
    explicit Variant(const TFirst& first, const TSecond& second);

    template <typename T>
    explicit Variant(const T& v);

    Variant& operator=(const Variant& v);
    Variant& operator=(Variant&& v) noexcept;

    DEF_OPERATOR_ASSIGN(int8_t, Type::BYTE_1)
    DEF_OPERATOR_ASSIGN(int16_t, Type::BYTE_2)
    DEF_OPERATOR_ASSIGN(int32_t, Type::BYTE_4)
    DEF_OPERATOR_ASSIGN(int64_t, Type::BYTE_8)
    DEF_OPERATOR_ASSIGN(uint8_t, Type::UBYTE_1)
    DEF_OPERATOR_ASSIGN(uint16_t, Type::UBYTE_2)
    DEF_OPERATOR_ASSIGN(uint32_t, Type::UBYTE_4)
    DEF_OPERATOR_ASSIGN(uint64_t, Type::UBYTE_8)
    DEF_OPERATOR_ASSIGN(double, Type::DOUBLE)
    DEF_OPERATOR_ASSIGN(bool, Type::BOOL)
    DEF_OPERATOR_ASSIGN(std::string&, Type::STRING)
    DEF_OPERATOR_ASSIGN(char*, Type::STRING)
    DEF_OPERATOR_ASSIGN(ByteArray_t&, Type::BYTEARRAY)
    DEF_OPERATOR_ASSIGN(VariantVector_t&, Type::VECTOR)
    DEF_OPERATOR_ASSIGN(VariantList_t&, Type::LIST)
    DEF_OPERATOR_ASSIGN(VariantMap_t&, Type::MAP)
    DEF_OPERATOR_ASSIGN(VariantPair_t&, Type::PAIR)

    template <typename T>
    DEF_OPERATOR_ASSIGN(T&, Type::CUSTOM)

    void clear();

    inline Type getType() const;

    operator bool() const;

    bool isEmpty() const;

    bool isNumeric() const;

    bool isSignedNumeric() const;

    bool isUnsignedNumeric() const;

    bool isBool() const;

    bool isString() const;

    bool isByteArray() const;

    bool isVector() const;
    bool isList() const;

    bool isMap() const;

    bool isPair() const;

    bool isCustomType() const;

    int64_t toInt64() const;

    uint64_t toUInt64() const;

    double toDouble() const;

    bool toBool() const;

    std::string toString() const;

    ByteArray_t toByteArray() const;

    std::shared_ptr<ByteArray_t> getByteArray() const;

    std::shared_ptr<VariantVector_t> getVector() const;

    template <typename T>
    std::vector<T> toVector(const std::function<T(const Variant&)>& converter) const;

    std::shared_ptr<VariantList_t> getList() const;

    template <typename T>
    std::list<T> toList(const std::function<T(const Variant&)>& converter) const;

    std::shared_ptr<VariantMap_t> getMap() const;

    template <typename K, typename V>
    std::map<K, V> toMap(const std::function<K(const Variant&)>& converterKey,
                         const std::function<V(const Variant&)>& converterValue) const;

    std::shared_ptr<VariantPair_t> getPair() const;

    template <typename TFirst, typename TSecond>
    std::pair<TFirst, TSecond> toPair(const std::function<TFirst(const Variant&)>& converterFirst,
                                      const std::function<TSecond(const Variant&)>& converterSecond) const;

    template <typename T>
    std::shared_ptr<T> getCustomType() const;

    bool operator==(const Variant& val) const;

    bool operator!=(const Variant& val) const;

    bool operator>(const Variant& val) const;

    bool operator>=(const Variant& val) const;

    bool operator<(const Variant& val) const;

    bool operator<=(const Variant& val) const;

private:
    Variant(std::shared_ptr<void> d, const Type t);

    template <typename T>
    Variant(const T& v, const Type t);

    bool isSameObject(const Variant& val) const;

    template <typename T>
    void assign(const T& v, const Type t);

    template <typename T>
    inline std::shared_ptr<T> value() const;

    template <typename T>
    void createMemoryAllocator();

    void freeMemory();

private:
    Type type = Type::UNKNOWN;
    std::shared_ptr<void> data;
    MemoryAllocatorFunc_t memoryAllocator;
    CompareFunc_t compareOperator;
};

template <typename T>
Variant Variant::make(const std::vector<T>& v) {
    return Variant(v);
}

template <typename T>
Variant Variant::make(const std::list<T>& v) {
    return Variant(v);
}

template <typename K, typename V>
Variant Variant::make(const std::map<K, V>& v) {
    return Variant(v);
}

template <typename TFirst, typename TSecond>
Variant Variant::make(const TFirst& first, const TSecond& second) {
    return Variant(first, second);
}

template <typename T>
Variant Variant::make(const T& v) {
    return makeCustom(v);
}

template <typename T>
Variant Variant::makeCustom(const T& v) {
    return Variant(v, Type::CUSTOM);
}

template <typename T>
Variant::Variant(const std::vector<T>& v) {
    std::shared_ptr<VariantVector_t> dest = std::shared_ptr<VariantVector_t>(new VariantVector_t(), [](void* ptr) {
        delete reinterpret_cast<VariantVector_t*>(ptr);
    });

    dest->reserve(v.size());

    for (auto it = v.begin(); it != v.end(); ++it) {
        dest->emplace_back(*it);
    }

    data = std::static_pointer_cast<void>(dest);
    type = Type::VECTOR;
    createMemoryAllocator<VariantVector_t>();
}

template <typename T>
Variant::Variant(const std::list<T>& v) {
    std::shared_ptr<VariantList_t> dest =
        std::shared_ptr<VariantList_t>(new VariantList_t(), [](void* ptr) { delete reinterpret_cast<VariantList_t*>(ptr); });

    for (auto it = v.begin(); it != v.end(); ++it) {
        dest->emplace_back(*it);
    }

    data = std::static_pointer_cast<void>(dest);
    type = Type::LIST;
    createMemoryAllocator<VariantList_t>();
}

template <typename K, typename V>
Variant::Variant(const std::map<K, V>& v) {
    std::shared_ptr<VariantMap_t> dest =
        std::shared_ptr<VariantMap_t>(new VariantMap_t(), [](void* ptr) { delete reinterpret_cast<VariantMap_t*>(ptr); });

    for (auto it = v.begin(); it != v.end(); ++it) {
        dest->emplace(it->first, it->second);
    }

    data = std::static_pointer_cast<void>(dest);
    type = Type::MAP;
    createMemoryAllocator<VariantMap_t>();
}

template <typename TFirst, typename TSecond>
Variant::Variant(const TFirst& first, const TSecond& second)
    // cppcheck-suppress misra-c2012-10.4 : false-positive. thinks that ':' is arithmetic operation
    : Variant(std::make_pair(Variant(first), Variant(second))) {}

template <typename T>
Variant::Variant(const T& v) {
    assign(v, Type::CUSTOM);
}

template <typename T>
Variant& Variant::operator=(const T& v) {
    assign(v, Type::CUSTOM);
    return *this;
}

Variant::Type Variant::getType() const {
    return type;
}

template <typename T>
std::shared_ptr<T> Variant::getCustomType() const {
    std::shared_ptr<T> result;

    if (true == isCustomType()) {
        result = value<T>();
    }

    return result;
}

template <typename T>
std::vector<T> Variant::toVector(const std::function<T(const Variant&)>& converter) const {
    std::vector<T> res;

    if (isVector()) {
        std::shared_ptr<VariantVector_t> vectorData = getVector();

        // cppcheck-suppress misra-c2012-14.4 ; false-positive. std::shared_ptr has a bool() operator
        if (vectorData) {
            res.reserve(vectorData->size());

            for (const Variant& curItem : *vectorData) {
                res.emplace_back(converter(curItem));
            }
        }
    }

    return res;
}

template <typename T>
std::list<T> Variant::toList(const std::function<T(const Variant&)>& converter) const {
    std::list<T> res;

    if (isList()) {
        std::shared_ptr<VariantList_t> listData = getList();

        // cppcheck-suppress misra-c2012-14.4 ; false-positive. std::shared_ptr has a bool() operator
        if (listData) {
            for (const Variant& curItem : *listData) {
                res.emplace_back(converter(curItem));
            }
        }
    }

    return res;
}

template <typename K, typename V>
std::map<K, V> Variant::toMap(const std::function<K(const Variant&)>& converterKey,
                              const std::function<V(const Variant&)>& converterValue) const {
    std::map<K, V> res;

    if (isMap()) {
        std::shared_ptr<VariantMap_t> mapData = getMap();

        // cppcheck-suppress misra-c2012-14.4 ; false-positive. std::shared_ptr has a bool() operator
        if (mapData) {
            for (const auto& curItem : *mapData) {
                res.insert(std::make_pair(converterKey(curItem.first), converterValue(curItem.second)));
            }
        }
    }

    return res;
}

template <typename TFirst, typename TSecond>
std::pair<TFirst, TSecond> Variant::toPair(const std::function<TFirst(const Variant&)>& converterFirst,
                                           const std::function<TSecond(const Variant&)>& converterSecond) const {
    std::pair<TFirst, TSecond> res;

    if (isPair()) {
        std::shared_ptr<VariantPair_t> pairData = getPair();

        // cppcheck-suppress misra-c2012-14.4 ; false-positive. std::shared_ptr has a bool() operator
        if (pairData) {
            res = std::make_pair(converterFirst(pairData->first), converterSecond(pairData->second));
        }
    }

    return res;
}

template <typename T>
Variant::Variant(const T& v, const Type t) {
    assign(v, t);
}

template <typename T>
void Variant::assign(const T& v, const Type t) {
    freeMemory();
    createMemoryAllocator<T>();
    type = t;
    data = memoryAllocator(&v);
}

template <typename T>
inline std::shared_ptr<T> Variant::value() const {
    return std::static_pointer_cast<T>(data);
}

template <typename T>
void Variant::createMemoryAllocator() {
    memoryAllocator = [](const void* ptr) {
        // NOTE: false-positive. "return" statement belongs to lambda function, not parent function
        // cppcheck-suppress misra-c2012-15.5
        return ((nullptr != ptr) ? (std::shared_ptr<void>(new T(*reinterpret_cast<const T*>(ptr)),
                                                          [](void* ptr) { delete reinterpret_cast<const T*>(ptr); }))
                                 : nullptr);
    };

    // cppcheck-suppress misra-c2012-13.1 ; false-positive. this is a functor, not initializer list
    compareOperator = [](const void* left, const void* right) {
        int res = -1;
        if (*reinterpret_cast<const T*>(left) == *reinterpret_cast<const T*>(right)) {
            res = 0;
        } else if (*reinterpret_cast<const T*>(left) > *reinterpret_cast<const T*>(right)) {
            res = 1;
        } else {
            // do nothing
        }

        // NOTE: false-positive. "return" statement belongs to lambda function, not parent function
        // cppcheck-suppress misra-c2012-15.5
        return res;
    };
}

}  // namespace hsmcpp

#endif  // HSMCPP_VARIANT_HPP