Отвечая на ваш вопрос
Я хотел бы иметь возможность добавить любую переменную или значение любого типа данных, включая классы, определенные пользователем.
Без сотрудничества с пользователем это невозможно в C ++.
Помните, что типы C ++ являются концепцией только во время компиляции.Они не существуют во время выполнения.Единственная информация о типе, доступная во время выполнения, это тонкий слой RTTI, предоставляемый typeid()
.Типизация утки во время выполнения, как в Python, невозможна.
Вы можете создать контейнер произвольных объектов довольно легко.
std::vector<std::any> v; // requires C++17
Однако пользователь этого контейнера должен знать, что содержит индекс, чтотип:
if (v[0].type() == typeid(ArbitraryUserType)) {
const auto& item = std::any_cast<ArbitraryUserType>(v[0]);
// work on item ...
}
Из-за природы типов во время компиляции вы как автор библиотеки не можете выполнить это any_cast
.Это должно быть прописано в исходном коде пользователя.
В общем, не пытайтесь внедрить питоническое мышление в C ++ .Это никогда не заканчивается хорошо, особенно когда вы пытаетесь обойти одну из самых основных основ C ++: его мощную систему статических типов.
Примечания:
- Без C ++ 17 вы могли быиспользуйте
boost::any
. - Если вы знаете список возможных типов во время компиляции,
std::vector<std::variant<Type1, Type2, etc>>
- хорошая альтернатива.С any
пользователь несет полную ответственность за отслеживание их типов.Поскольку все проверки типов происходят во время выполнения, компилятор не может помочь.Variant
, с другой стороны, возвращает большую часть безопасности во время компиляции.И снова есть boost::variant
в качестве альтернативы, отличной от C ++ 17.
Замечания о подходе к кодированию
В основном вы пытаетесь сериализовать (кодировать) и десериализовать (декодировать)) произвольные типы.Без сотрудничества с этими типами это невозможно.
Ваш подход работает только для тривиальных типов, которые можно копировать по крупицам.В C ++ даже есть черта типа: std :: is_trivially_copyable .В конце концов, вы поддерживаете базовые типы и структуры в стиле Си, но ничего больше.
Представьте, что T
для вашей функции encode()
было std::string
.Проще говоря, std::string
содержит указатель на отдельно выделенный фрагмент памяти, где хранятся фактические строковые данные.Сам строковый объект является просто управляющей оболочкой для этого указателя.encode()
только сериализует объект-оболочку, но не указывает на блок памяти с фактическими данными.
Даже если во время десериализации вы можете создать экземпляры произвольных типов из потока битов, поток не завершен.Вам нужно реализовать C ++ версию Python copy.deepcopy
, которая невозможна без взаимодействия с каждым типом.Взгляните на библиотеку сериализации C ++ - возьмите Cereal в качестве простого примера - чтобы увидеть, как это сотрудничество может выглядеть на практике.