В C ++, как вы можете избежать явных откликов с картой универсальных типов? - PullRequest
3 голосов
/ 13 августа 2010

Это немного надумано, но, скажем, у меня есть интерфейс класса, подобный этому:

class IResource;
class IResourceContainer
{
 public:
    virtual ~IResourceContainer() {}
    virtual void AddResource(const std::string& rStrName, 
                             std::auto_ptr<IResource> apResource)=0;
    virtual IResource& GetResource(const std::string& rStrName)=0; 
};

, и у меня есть реализация этого класса, которая содержит карту строк для типов IResource.Если бы я добавил свой собственный ресурс, как это:

container.AddResource("foo", std:auto_ptr<IResource>( new CFooResource);

, а затем позже получил бы ссылку на ресурс

CFooResource& fooResource = container.GetResource(); // error

Это не скомпилировалось бы, так как мне нужно было бы уменьшить IResource доCFooResource.Я думал о том, чтобы скрыть это, заставляя GetResource принимать параметр шаблона, который внутренне понижает тип, но, очевидно, шаблоны и чистые интерфейсы не объединяются.Моя текущая альтернатива состоит в том, чтобы скрыть приведение в функции CastResource, которая вызывает boost :: polymorphic_downcast, но я все еще не в восторге от идеи, что клиенту нужно будет привести это свойство к ресурсу.*

CFooResource& fooResource = CastResource<CFooResource&>(container.GetResource());

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

Спасибо.

Ответы [ 5 ]

5 голосов
/ 13 августа 2010

есть ли лучший способ удерживать указатели на универсальные типы, которые не требуют явных откликов от пользователя?

Нет.
Либо вы используете классический ОО,он же полиморфизм времени исполнения .Тогда вы застряли с интерфейсами базового класса или вам придется обманывать и понижать.Или вы используете шаблоны, такие как полиморфизм времени компиляции .Затем вы связаны с одним типом ресурса во время компиляции.

Существуют способы немного размыть границу между ними (например, boost::any), но в основном это то, что вам нужно решить.

3 голосов
/ 13 августа 2010
class ResourceWrapper {
private:
    IResource *resource;

public:
    ResourceWrapper() { }
    ResourceWrapper(IResource *resource) : resource(resource) { }
    ResourceWrapper(ResourceWrapper wrapper) : resource(wrapper.resource) { }

    template <class T>
    T &As()
    {
        if (resource == NULL) return NULL;
        T *ret = dynamic_cast<T*>(resource);
        if (ret == NULL) throw Exception("wrong resource type");
        return ret;
    }
};

class IResourceContainer
{
public:
    virtual ~IResourceContainer() {}
    virtual void AddResource(const std::string& rStrName, 
    std::auto_ptr<IResource> apResource)=0;
    virtual ResourceWrapper GetResource(const std::string& rStrName)=0; 
};

CFooResource& fooResource = container.GetResource("name").As<CFooResource>();

или

class ResourceWrapper {
private:
    IResource *resource;

public:
    ResourceWrapper() { }
    ResourceWrapper(IResource *resource) : resource(resource) { }
    ResourceWrapper(ResourceWrapper wrapper) : resource(wrapper.resource) { }

    template <class T>
    void Get(T **ret)
    {
        *ret = dynamic_cast<T*>(resource);
        /* optionally throw exception when dynamic_cast fails */
    }
};

class IResourceContainer
{
public:
    virtual ~IResourceContainer() {}
    virtual void AddResource(const std::string& rStrName, 
    std::auto_ptr<IResource> apResource)=0;
    virtual ResourceWrapper Resource(const std::string& rStrName)=0; 
};

CFooResource *fooResource;
container.Resource("name").Get(&fooResource);
3 голосов
/ 13 августа 2010

Возможно, вы пытаетесь решить не ту проблему.

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

1 голос
/ 13 августа 2010

Если целью контейнера ресурсов является хранение только указателей, то вы можете сделать его шаблоном класса

0 голосов
/ 14 августа 2010

Если количество различных типов ресурсов невелико и относительно фиксировано, вы можете просто создать дополнительные методы для вашего контейнера ресурсов, например, IStringResource& GetStringResource(string name).Внутри вы можете хранить каждый тип ресурса на отдельной карте с более конкретным типом ресурса.Это дает вам полную безопасность во время компиляции относительно downcast.

Я не могу придумать другой способ избежать приведения.Однако, так как вы возвращаете ссылки на свои ресурсы, вам все равно придется обрабатывать случай, когда ресурс не найден.Это должно быть обработано путем генерирования исключения, поскольку вы не можете вернуть 0. Если вы все равно собираетесь выбросить исключение, вы можете упростить свою жизнь, создав метод шаблона.

T& GetResource<T>(string name) 
{
    IResource* res = ...fetch resource...
    T* tres = dynamic_cast<T*>(res);
    if (tres == 0)
    {
         throw ResourceNotFoundException();
    }
    return *tres;
}

Я немного устал от синтаксиса моего шаблона, но я надеюсь, что это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...