Implementing a type-safe heterogeneous container using RTTI involves creating a container that can store objects of different types while providing type-safe access to those objects. Here's an approach to create such a container:
Define a base class for stored objects:
#include <typeinfo>
class Storable {
public:
virtual ~Storable() = default;
virtual const std::type_info& getType() const = 0;
};
Create a template class for storing specific types:
#include <typeinfo>
class Storable {/*...*/};
template <typename T>
class StorableObject : public Storable {
public:
StorableObject(const T& obj)
: object_(obj) {}
const std::type_info& getType() const override {
return typeid(T);
}
const T& get() const { return object_; }
private:
T object_;
};
Implement the heterogeneous container:
#include <memory>
#include <string>
#include <typeinfo>
#include <typeindex>
#include <unordered_map>
#include <vector>
#include <stdexcept>
class Storable {/*...*/};
class StorableObject : public Storable {/*...*/};
class HeterogeneousContainer {
public:
template <typename T>
void add(const T& object) {
auto storable = std::make_unique<
StorableObject<T>>(object);
objects_.push_back(std::move(storable));
typeMap_[std::type_index(typeid(T))].push_back(
objects_.size() - 1
);
}
template <typename T>
const T& get(size_t index) const {
auto it = typeMap_.find(
std::type_index(typeid(T)));
if (it == typeMap_.end()
|| index >= it->second.size()
) {
throw std::out_of_range(
"Invalid index or type"
);
}
size_t objIndex = it->second[index];
auto* ptr = dynamic_cast<StorableObject<T>*>(
objects_[objIndex].get());
if (!ptr) {
throw std::runtime_error("Type mismatch");
}
return ptr->get();
}
template <typename T>
size_t count() const {
auto it = typeMap_.find(
std::type_index(typeid(T)));
return (
it != typeMap_.end())
? it->second.size()
: 0;
}
private:
std::vector<std::unique_ptr<Storable>> objects_;
std::unordered_map<
std::type_index, std::vector<size_t>> typeMap_;
};
Use the heterogeneous container:
#include <iostream>
#include <memory>
#include <string>
#include <typeinfo>
#include <typeindex>
#include <unordered_map>
#include <vector>
#include <stdexcept>
class Storable {/*...*/};
class StorableObject : public Storable {/*...*/};
class HeterogeneousContainer {/*...*/};
int main() {
HeterogeneousContainer container;
container.add(42);
container.add(3.14);
container.add(std::string("Hello"));
container.add(100);
std::cout << "Integers: "
<< container.count<int>() << '\n';
std::cout << "Floats: "
<< container.count<double>() << '\n';
std::cout << "Strings: "
<< container.count<std::string>() << '\n';
std::cout << "First int: "
<< container.get<int>(0) << '\n';
std::cout << "Second int: "
<< container.get<int>(1) << '\n';
std::cout << "Float: "
<< container.get<double>(0) << '\n';
std::cout << "String: "
<< container.get<std::string>(0) << '\n';
try {
container.get<float>(0);
} catch (const std::exception& e) {
std::cout << "Error: " << e.what() << '\n';
}
}
Integers: 2
Floats: 1
Strings: 1
First int: 42
Second int: 100
Float: 3.14
String: Hello
Error: Invalid index or type
This implementation provides several benefits:
get<T>()
method ensures type-safe access to stored objects.typeMap_
allows for quick lookups of objects by type.Keep in mind that this implementation uses dynamic allocation and type erasure, which can have performance implications. For performance-critical code, you might consider alternatives like std::variant
or a compile-time heterogeneous container using template metaprogramming techniques.
Answers to questions are automatically generated and may not have been reviewed.
typeid()
Learn to identify and react to object types at runtime using RTTI, dynamic casting and the typeid()
operator