Search code examples
c++dynamic-cast

How can I use dynamic_cast to get objects with a user-specified type?


If there is a method, which aims to put objects with specific type into another list, how can I use a user-defined parameter in the dynamic_cast? I know, that I can not use the std::string parameter directly in dynamic_cast, but is there a better solution?

std::list<Car*> GetCarsOfType(std::string type)
    {
        std::list<Car*> carsOfType;

        for (int i = 0; i < cars.size(); ++i)
        {
            if (dynamic_cast<type> cars[i]) // This is not possible, but how can I solve this in a better way?
            {
                carsOfType.push_back(cars[i]);
            }
        }

        return carsOfType;
    }

Solution

  • You could keep a central repository of the mapping between the named car type and the actual type if you wish. It could then be used to do the dynamic_cast test for you.

    Example:

    #include <algorithm>
    #include <unordered_map>
    
    std::list<Car*> GetCarsOfType(std::string type) {
        // map between the named type and the dynamic_cast test:
        static const std::unordered_map<std::string, Car*(*)(Car*)> cast_checkers{
            {"SUV", [](Car* c) -> Car* { return dynamic_cast<SUV*>(c); }},
            {"Limo", [](Car* c) -> Car* { return dynamic_cast<Limo*>(c); }}
        };
    
        std::list<Car*> carsOfType;
    
        // a filter that only returns true for those you want
        auto flt = [&type](Car* c) { return cast_checkers.at(type)(c) != nullptr; };
    
        std::ranges::copy_if(cars, std::back_inserter(carsOfType), flt);
    
        return carsOfType;
    }
    

    Or using a view:

    #include <ranges>
    #include <unordered_map>
    
    std::list<Car*> GetCarsOfType(std::string type) {
        static std::unordered_map<std::string, Car* (*)(Car*)> cast_checkers{
            {"SUV", [](Car* c) -> Car* { return dynamic_cast<SUV*>(c); }},
            {"Limo", [](Car* c) -> Car* { return dynamic_cast<Limo*>(c); }}};
    
        // same filter as above:
        auto flt = [&type](Car* c) { return cast_checkers.at(type)(c) != nullptr; };
    
        // a view over those you want
        auto matchview = cars | std::views::filter(flt);
    
        // populate the container using the view:
        return {matchview.begin(), matchview.end()};
    }