In C ++, how can you avoid explicit downcasts with a generic type map?

This is a bit far-fetched, but I will say that I have such a class interface:

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; 
};

and I have an implementation of this class that contains a string map for IResource types. If I added my own resource as follows:

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

and then get a link to the resource

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

This will not compile since I will need to knock down IResource on CFooResource. I thought about hiding this by forcing GetResource to accept a template parameter that omits the type inside, but obviously templates and clean interfaces do not jive. My alternative is to hide the casting in the CastResource function, which calls boost :: polymorphic_downcast, but I'm still not happy with the idea that the client will need to use the resource.

For instance:

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

, , : , ? , , . , , , .

.

+3
5

, ?

.
OO, a.k.a. . , . , a.k.a. . .

(boost::any, ), , .

+5

, .

IResource ? , IResource, , .

+3
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

, ,

+1

, , IStringResource& GetStringResource(string name). . .

I can’t think of another way to avoid the cast. However, since you are returning links to your resources, you still have to handle the case when the resource is not found. This must be handled by throwing an exception, since you cannot return 0. If you are still going to throw an exception, you can make your life easier by creating a boilerplate method.

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

I'm a little rusty with my template syntax, but hope this makes sense.

0
source

Source: https://habr.com/ru/post/1759568/


All Articles