Reputation: 886
I love coding, and generally do so in Python due to its simplicity and power.
However, for some time critical programs/tasks, I use C++.
Therefore, to get best of both worlds, I am making a Pythonesque list in C++.
AIM: I would like to be able to add any variable or value of any data type, including classes user has defined.
To do this, I have a structure item
with a char * value
, a char * type
and an int size
.
My List
has an array of these item *
s.
Now, I have taken the variable in a template function:
template<class T> item * encode(const T& var);
and declared a pointer to item item * i = new item;
And, I have stored the values of these variables as c style strings.
For example, 14675
in binary is 0000 0000 0000 0000 0011 1001 0101 0011
Therefore, I have dynamically created space, like so:
i->size = sizeof(var);
i->value = new char[i->size]; //4 in this case
and set each bit in value with respective bits in var.
I have also stored their types as
i->type = typeinfo(var).name();
So far so good!
Now, I am stuck with auto decode(item * i) -> decltype(/*What goes here???*/)
How do I specify the return type of the function?
Is there any possible way?
Preferably using the i->type
?
Or, should I make changes in the basic design of this process?
Thanks in advance!
Upvotes: 1
Views: 1565
Reputation: 2647
I would like to be able to add any variable or value of any data type, including classes user has defined.
Without cooperation from the user that’s impossible in C++.
Remember that C++ types are a compile-time concept only. They do not exist at runtime. The only type information available at runtime is the thin layer of RTTI provided by typeid()
. Runtime duck-typing like in Python is not possible.
You can create a container of arbitrary objects quite easily.
std::vector<std::any> v; // requires C++17
However the user of that container has to know what index contains what type:
if (v[0].type() == typeid(ArbitraryUserType)) {
const auto& item = std::any_cast<ArbitraryUserType>(v[0]);
// work on item ...
}
Because of the compile-time nature of types you as the library writer cannot perform that any_cast
. It has to be spelled out in the user’s source code.
In general, don’t try to shoehorn a pythonic mindset into C++. It never ends well, especially when you try to circumvent one of the most basic foundations of C++: its powerful static type system.
Notes:
boost::any
.std::vector<std::variant<Type1, Type2, etc>>
is a good alternative. With any
the user is fully responsible to keep track of their types. Because all type checks happen at runtime the compiler cannot help. Variant
on the other hand brings back a large chunk of the compile-time safety. And again there’s boost::variant
as a non-C++17 alternative.Basically you’re trying to serialize (encode) and deserialize (decode) arbitrary types. Without cooperation from those types, that’s not possible.
Your approach only works for trivial types that can be copied bit by bit. C++ even has a type trait for that: std::is_trivially_copyable. In the end you support fundamental types and C-style structs of those, but nothing else.
Imagine the T
for your encode()
function was std::string
. Simply put a std::string
contains a pointer to a separately allocated piece of memory where the actual string data is stored. The string object itself is just a managing wrapper for that pointer. encode()
only serializes the wrapper object, but not the pointed-to memory block with the actual data.
Even if during deserialization you could instantiate arbitrary types from a stream of bits, the stream is not complete. What you’d have to implement is a C++ version of Python’s copy.deepcopy
, which is impossible without cooperation from each type. Have a look at a C++ serialization library – take Cereal as a straight-forward example – to see how that cooperation can look in practice.
Upvotes: 1