У меня есть эта карта в моей Entity-Component-System:

std::map<u_int32_t, std::vector<std::shared_ptr<Component>>> _componentMap;

u_int32_t - это ключ к вектору компонентов. Может быть несколько экземпляров одного и того же компонента. (Вот почему есть вектор).

Теперь я хотел бы иметь шаблонную функцию-получатель, которая возвращает вектор унаследованного типа:

template<class T> inline const std::vector<std::shared_ptr<T>> & getVector() const
{
     u_int32_t key = getKey<T>();
     return static_cast<std::vector<std::shared_ptr<T>>>(_componentMap.count(key) ? _componentMap.at(key) : _emptyComponentVec);
}

Я знаю, что это не работает, поскольку std::vectors разных типов совершенно не связаны между собой, и я не могу использовать приведение между ними. Я также хотел бы избежать выделения нового вектора при каждом вызове этой функции.

Но как я могу добиться желаемого поведения? Когда компоненты добавлены, я могу создать std::vector желаемого производного типа.

Также может возникнуть вопрос: как получить std::map, содержащий различные типы std::vector?

Для любых решений я не могу связать с boost, хотя, если это абсолютно необходимо, я мог бы интегрировать отдельные заголовки boost.

0
keyboard 8 Май 2016 в 14:55

3 ответа

Лучший ответ

Решением было использовать reinterpret_cast:

    template<class T> inline std::vector<std::shared_ptr<T>> * getVector() const
    {
        auto key = getKey<T>();
        return reinterpret_cast<std::vector<std::shared_ptr<T>> *>( (_componentMap.count(key) ? _componentMap.at(key).get() : const_cast<std::vector<std::shared_ptr<Component>> *>(&_emptyComponentSharedPtrVec)) );
    }

Это не очень красиво, но работает нормально и удовлетворяет всем требованиям.

0
keyboard 22 Май 2016 в 11:57
template<class It>
struct range_view {
  It b, e;
  It begin() const { return b; }
  It end() const { return e; }
  using reference = decltype(*std::declval<It const&>());
  reference operator[](std::size_t n) const
  {
    return b[n];
  }
  bool empty() const { return begin()==end(); }
  std::size_t size() const { return end()-begin(); }
  reference front() const {
    return *begin();
  }
  reference back() const {
    return *std::prev(end());
  }
  template<class O>
  range_view( O&& o ):
    b(std::begin(o)), e(std::end(o))
  {}
};

Это быстрый просмотр диапазона. Это можно улучшить.

Теперь все, что вам нужно сделать, это написать итератор с псевдослучайным доступом, который преобразует свои аргументы. Таким образом, требуется итератор произвольного доступа к типу T, а затем выполняется некоторая операция F, чтобы вернуть тип U. Он пересылает все другие операции.

Затем map сохраняет std::vector<std::shared_ptr<Base>>. Геттор возвращает range_view< converting_iterator<spBase2spDerived> >.

1
Yakk - Adam Nevraumont 9 Май 2016 в 01:05

Вот примерная реализация решения, которое я придумал для этой проблемы. Конечно, есть много возможностей для доработки кода, но, надеюсь, он передает мою идею.

#include <iostream>
#include <map>
#include <vector>
#include <memory>
using namespace std;

class Base {
public:
    virtual void f() const = 0;
};

class A : public Base {
public:
    static const int type = 0;
    explicit A(int a) : a_(a) {}
    void f() const { cout << "calling A::f" << endl;}
    int a_;
};
class B : public Base {
public:
    static const int type = 1;
    explicit B(int a) : a_(a) {}
    void f() const { cout << "calling B::f" << endl;}
    int a_;
};

class MapWrapper {
public:
    template<class T>
    void append(int a, vector<T> const& vec) {
        types_[a] = T::type;
        my_map_[a] = make_shared<vector<T>>(vec);
    }
    template<class T>
    vector<T> const& get(int a) const {
        return *static_pointer_cast<vector<T>>( my_map_.at(a) );
    }
    map<int, shared_ptr<void>> const& get_my_map() const {
        return my_map_;
    }
    vector<shared_ptr<Base>> get_base(int a) const {
        vector<shared_ptr<Base>> ret;
        switch(types_.at(a)) {
            case 0: {
                auto const vec = get<A>(a);
                for(auto v : vec)
                    ret.push_back(make_shared<A>(v));
                break;  
            }
            case 1: {
                auto const vec = get<B>(a);
                for(auto v : vec)
                    ret.push_back(make_shared<B>(v));
                break;  
            }
        }
        return ret;
    }
    map<int, shared_ptr<void>> my_map_;
    map<int, int> types_;
};  

int main() {
    MapWrapper map_wrapper;
    map_wrapper.append(10, vector<A>{A(2), A(4)});
    map_wrapper.append(20, vector<B>{B(5), B(7), B(9)});

    for(auto const& w : map_wrapper.get_my_map())
        for(auto v : map_wrapper.get_base(w.first))
            v->f();

    for(auto const& x: map_wrapper.get<A>(10))
        cout << x.a_ << " ";
    cout << endl;

    for(auto const& x: map_wrapper.get<B>(20))
        cout << x.a_ << " ";

    return 0;
}
1
Eissa N. 14 Май 2016 в 21:50