brainsandwich brainsandwich - 1 month ago 7
C++ Question

Applying a function to contained objects, provided they derive from a base type

I'm having a hard time synthesizing my problem and I'm not sure the title summarizes it very well :/

I'm trying to keep an map of polymorphic containers in a structure in order to have a nice flat array of objects.

My trouble is that I want to apply a function on the objects contained, but only those derived from a specified Base type. Down there is a stripped down version of what I am trying to achieve.

struct BaseStock {};

template <typename ContainedType>
struct Stock : BaseStock {
std::vector<ContainedType> container;
};

struct Store {
std::unordered_map<std::type_index, std::unique_ptr<BaseStock>> stocks;

template <typename Base>
void something() {
for (auto& pair: stocks) {
// Here, do somehting only if pair.second's
// contained type derives from Base
}
}
};


I tried to access contained elements from
BaseStock
by returning
void*
but it's impossible to
dynamic_cast
from it, thus disallowing a type check. The
stocks
map keeps the
type_index
of the Stock's contained type but it's impossible to retrieve hierarchy information from that either.

Allowing the
BaseStock
to return something that could be checked against seemed to be the closest solution, but it would mean to force the
ContainedType
to always derive from something (which is not bad at all, but I like the idea of a container that doesn't imposes restrictions on the types you're using)

Thank you very much for your help !

Answer

I think this does what you want, feel free to request a larger explanation on something you don't get in the comments.

#include <vector>
#include <iostream>
#include <unordered_map>
#include <typeindex>
#include <memory>

struct BaseStock {};
template <typename ContainedType>
struct Stock : BaseStock {
    std::vector<ContainedType> container;
};

struct Store {
    std::unordered_map<std::type_index, std::unique_ptr<BaseStock>> stocks;
    template <typename Base>
    void something() {
        for (auto& pair: stocks) {
            if (typeid(pair.first) != typeid(Base)) {
                std::cout << "Checking via typeid.\n";
            }
        }
    }
};

template<typename BaseType, typename DerivedType = BaseType> 
void InsertIntoStore(Store* store) {
    store->stocks[typeid(BaseType)] =
        std::unique_ptr<BaseType>(std::make_unique<DerivedType>());
} 

int main() {
    Store store;
    // Put two things into the map.
    InsertIntoStore<BaseStock, Stock<int>>(&store);
    InsertIntoStore<BaseStock>(&store);      
    // But only get one output.
    store.something<BaseStock>();
}