Re[7]: Про перемещение (на примере кода)
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 14.04.25 14:01
Оценка: :)
Здравствуйте, Shmj, Вы писали:

S>Сетевой бинарный пакет. С помощью методов устанавливаем удобным образом отдельные части пакета — как то версия, тип пакета и пр. Но так же должна быть возможность получить данные пакета целиком для отправки (или предварительного шифрования).


Знакомый делал тестовое задание, кидал мне посмотреть. Я у него разрешение спросил, он сказал, что можно опубликовать. Так что
  смотри и учись
/*
################################################################################
# Описание бинарного протокола
################################################################################

По сети ходят пакеты вида
packet : = size payload

size - размер последовательности, в количестве элементов, может быть 0.

payload - поток байтов(blob)
payload состоит из последовательности сериализованных переменных разных типов :

Описание типов и порядок их сериализации

type : = id(uint64_t) data(blob)

data : =
    IntegerType - uint64_t
    FloatType - double
    StringType - size(uint64_t) blob
    VectorType - size(uint64_t) ...(сериализованные переменные)

Все данные передаются в little endian порядке байтов

Необходимо реализовать сущности IntegerType, FloatType, StringType, VectorType
Кроме того, реализовать вспомогательную сущность Any
Сделать объект Serialisator с указанным интерфейсом.

Конструкторы(ы) типов IntegerType, FloatType и StringType должны обеспечивать инициализацию, аналогичную инициализации типов uint64_t, double и std::string соответственно.
Конструктор(ы) типа VectorType должен позволять инициализировать его списком любых из перечисленных типов(IntegerType, FloatType, StringType, VectorType) переданных как по ссылке, так и по значению.
Все указанные сигнатуры должны быть реализованы.
Сигнатуры шаблонных конструкторов условны, их можно в конечной реализации делать на усмотрение разработчика, можно вообще убрать.
Vector::push должен быть именно шаблонной функцией. Принимает любой из типов: IntegerType, FloatType, StringType, VectorType.
Serialisator::push должен быть именно шаблонной функцией.Принимает любой из типов: IntegerType, FloatType, StringType, VectorType, Any
Реализация всех шаблонных функций, должна обеспечивать constraint requirements on template arguments, при этом, использование static_assert - запрещается.
Код в функции main не подлежит изменению. Можно только добавлять дополнительные проверки.

Архитектурные требования :
1. Стаедарт - c++17
2. Запрещаются виртуальные функции.
3. Запрещается множественное и виртуальное наследование.
4. Запрещается создание каких - либо объектов в куче, в том числе с использованием умных указателей.
   Это требование не влечет за собой огранечений на использование std::vector, std::string и тп.
5. Запрещается любое дублирование кода, такие случаи должны быть строго обоснованы. Максимально использовать обобщающие сущности.
   Например, если в каждой из реализаций XType будет свой IdType getId() - это будет считаться ошибкой.
6. Запрещается хранение value_type поля на уровне XType, оно должно быть вынесено в обобщающую сущность.
7. Никаких других ограничений не накладывается, в том числе на создание дополнительных обобщающих сущностей и хелперов.
8. XType должны реализовать сериализацию и десериализацию аналогичную Any.

Пример сериализации VectorType(StringType("qwerty"), IntegerType(100500))
{0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x71,0x77,0x65,0x72,0x74,0x79,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x88,
 0x01,0x00,0x00,0x00,0x00,0x00}
*/

#include <iostream>
#include <vector>
#include <fstream>
#include <type_traits>
#include <variant>
#include <algorithm>


using Id = uint64_t;
using Buffer = std::vector<std::byte>;

enum class TypeId : Id {
    Uint,
    Float,
    String,
    Vector
};


class Any {
private: 

    using DataType = std::variant<uint64_t, double, std::string, std::vector<Any>>;
    static constexpr TypeId idToType[] = {TypeId::Uint, TypeId::Float, TypeId::String, TypeId::Vector};
    static constexpr size_t typeToId(TypeId type)
    {
        for(auto id = 0; id < std::size(idToType); id++)
        {
            if(idToType[id] == type)
            {
                return id;
            }
        }
        return -1;
    }

public:

    template<typename ...Args, std::enable_if_t<std::is_constructible_v<DataType, Args...>, bool> = false>
    Any(Args&& ... args) : m_place(std::forward<Args...>(args...))
    {}

    Any() = default;

    void serialize(Buffer& _buff) const
    {
        auto append = [&_buff](auto && val, size_t size = sizeof(val)){
            _buff.resize(_buff.size() + size);

            auto * begin = reinterpret_cast<const std::byte *>(&val);
            if constexpr (std::is_pointer_v<std::decay_t<decltype(val)>>)
            {
                begin = reinterpret_cast<const std::byte *>(val);
            }
            
            std::copy(begin, begin + size, _buff.end() - size);
        };
        auto type = getPayloadTypeId();
        append(type);
        switch (type)
        {
        case TypeId::Uint:
        {
            append(std::get<uint64_t>(m_place));
            return;
        }
        case TypeId::Float:
        {
            append(std::get<double>(m_place));
            return;
        }
        case TypeId::String:
        {
            const auto & str = std::get<std::string>(m_place);
            uint64_t stringLength = str.size();
            append(stringLength);
            append(str.data(), stringLength);

            return;
        }
        case TypeId::Vector:
        {
            const auto & vec = std::get<std::vector<Any>>(m_place);
            uint64_t vectorLength = vec.size();
            append(vectorLength);
            for(const auto & v : vec)
            {
                v.serialize(_buff);
            }
            return;
        }
        
        default:
            break;
        }
        return;
    }

    Buffer::const_iterator deserialize(Buffer::const_iterator _begin, Buffer::const_iterator _end)
    {
        // TODO: need to add actions on failed deseriasization 
        // All types takes at least 2 * sizeof(uint64_t) bytes 
        // (TypeId + value) or (TypeId + Length)
        auto hasEnoughtSpace = [&_begin, &_end](auto size){return _end - _begin >= size;};
        if(!hasEnoughtSpace(sizeof(Id) + sizeof(uint64_t)))
        {
            return _end;
        }

        auto convert = [&_begin](auto * val){
            std::decay_t<decltype(*val)> result;
            std::copy(_begin, _begin + sizeof(result), reinterpret_cast<std::byte *>(&result));
            _begin += sizeof(result);
            return result;
        };

        // TODO: replace with template lambda from C++ 20
        auto type = convert((TypeId *)(nullptr));
        
        switch (type)
        {
        case TypeId::Uint:
        {
            m_place.emplace<uint64_t>(convert((uint64_t *)(nullptr)));
            break;
        }
        case TypeId::Float:
        {
            m_place.emplace<double>(convert((double *)(nullptr)));
            break;
        }
        case TypeId::String:
        {
            auto stringLength = convert((uint64_t *)(nullptr));
             
            if(!hasEnoughtSpace(stringLength))
            {
                return _end;
            }
            m_place.emplace<std::string>(reinterpret_cast<const char *>(&(*_begin)), stringLength);
            _begin += stringLength;
            break;
        }
        case TypeId::Vector:
        {
            auto vectorLength = convert((uint64_t *)(nullptr));
            if(!hasEnoughtSpace(vectorLength))
            {
                return _end;
            }
            m_place.emplace<std::vector<Any>>(vectorLength);
            for(auto & any : std::get<std::vector<Any>>(m_place))
            {
                _begin = any.deserialize(_begin, _end);
            }
            break;
        }
        default:
            break;
        }
        return _begin;
    }

    TypeId getPayloadTypeId() const
    {
        return idToType[m_place.index()];
    }

    template<typename Type>
    auto& getValue() const
    {
        return std::get<Type>(m_place);
    }

    template<TypeId kId>
    auto& getValue() const
    {
        return std::get<typeToId(kId)>(m_place);
    }

    bool operator == (const Any& _o) const
    {
        return (_o.m_place == m_place);
    }

private:

    DataType m_place;
};

class IntegerType : public Any {
public:
    //TODO: replace with pretty concepts (c++20) instead of enable if
    template<typename Arg, std::enable_if_t<std::is_constructible_v<uint64_t, Arg>, bool> = false>
    explicit IntegerType(Arg && arg) : Any{std::in_place_type_t<uint64_t>{}, std::forward<Arg>(arg)}
    {
 
    }
};

class FloatType : public Any {
public:
    template<typename Arg, std::enable_if_t<std::is_constructible_v<double, Arg>, bool> = false>
    explicit FloatType(Arg && arg) : Any(std::in_place_type_t<double>{}, std::forward<Arg>(arg))
    {

    }
};

class StringType : public Any {
public:
    template<typename ...Args, std::enable_if_t<std::is_constructible_v<std::string, Args ...>, bool> = false>
    explicit StringType(Args&& ... args) : Any(std::in_place_type_t<std::string>{}, std::forward<Args...>(args...))
    {}
};

class VectorType;

template<typename T>
static constexpr bool IsValidType = std::is_same_v<IntegerType, std::decay_t<T>> ||
                                    std::is_same_v<FloatType, std::decay_t<T>> ||
                                    std::is_same_v<StringType, std::decay_t<T>> ||
                                    std::is_same_v<VectorType, std::decay_t<T>> ||
                                    std::is_same_v<Any, std::decay_t<T>>;

class VectorType : public Any {
public:
    template<typename ...Args, std::enable_if_t<(IsValidType<Args> && ...), bool> = false>
    VectorType(Args&& ...args) : Any(std::in_place_type_t<std::vector<Any>>{}, std::forward<Args...>(args...))
    {

    }

    template<typename Arg, std::enable_if_t<(IsValidType<Arg>), bool> = false>
    void push_back(Arg&& _val)
    {
        getValue<TypeId::Vector>().emplace_back(std::forward<Arg>(_val));
    }
};


class Serializator {
public:
    template<typename Arg, std::enable_if_t<(IsValidType<Arg>), bool> = false>
    void push(Arg&& _val)
    {
        m_data.emplace_back(std::forward<Arg>(_val));
    }

    Buffer serialize() const
    {
        Buffer result(sizeof(uint64_t));
        uint64_t vectorSize = m_data.size();
        std::copy(reinterpret_cast<std::byte *>(&vectorSize), reinterpret_cast<std::byte *>(&vectorSize) + sizeof(vectorSize), result.begin());
        for(auto & any : m_data)
        {
            any.serialize(result);
        }
        return result;
    }

    static std::vector<Any> deserialize(const Buffer& _val)
    {        
        if(_val.size() < sizeof(uint64_t))
        {
            return {};
        }
        uint64_t size;
        std::copy(_val.begin(), _val.begin() + sizeof(uint64_t), reinterpret_cast<std::byte *>(&size));

        if(size == 0)
        {
            return {};
        }

        auto begin = _val.begin() + sizeof(uint64_t);

        std::vector<Any> result(size);
        for(auto & any : result)
        {
            begin = any.deserialize(begin, _val.end());
        }
        return result;
    }

    const std::vector<Any>& getStorage() const
    {
        return m_data;
    }

private:
    std::vector<Any> m_data;
};


int main() {
    std::ifstream raw;
    raw.open("raw.bin", std::ios_base::in | std::ios_base::binary);
    if (!raw.is_open())
        return 1;
    raw.seekg(0, std::ios_base::end);
    std::streamsize size = raw.tellg();
    raw.seekg(0, std::ios_base::beg);

    Buffer buff(size);
    raw.read(reinterpret_cast<char*>(buff.data()), size);

    auto res = Serializator::deserialize(buff);

    Serializator s;
    for (auto&& i : res)
        s.push(i);

    std::cout << (buff == s.serialize()) << '\n';

    return 0;
}
Маньяк Робокряк колесит по городу
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.