【问题标题】:Converting a variable amount of arguments into an explicit amount将可变数量的参数转换为显式数量
【发布时间】:2017-09-08 17:46:06
【问题描述】:

我正在尝试实现一个资源管理器,它管理特定的加载器并提供一个 load() 函数,该函数重定向到加载器的各个 load() 函数。管理器还进行某种垃圾回收,例如通过调用每个加载器的 freeUnusedResources() 方法来卸载未使用的资源。

问题:
将可变数量的参数转换为显式数量。
我希望每个加载器实现能够提供具有固定数量参数的不同加载函数,而接口/基类 IResourceLoader 可以处理可变数量,这是单个 load( ) ResourceManager 的函数。

比如:

class IResource;   //Resources hold only data
class AResource : public IResource {};
class IResourceLoader {     //Loader load resources
    virtual IResource* load(...);
    //or
    template<typename ...Args>
    IResource* load(Args ...args);

    virtual void freeUnusedResources() = 0;
};

class ALoader : public IResourceLoader {
    std::vector<IResource*> _loadedResources;

    IResource* load(std::string path);  //"Implementing" IResourceLoader::load(...)
    IResource* load(std::string path, int a, int b); //"Implementing" IResourceLoader::load(...)
    void freeUnusedResources() override;
};

class ResourceManager {
    //A map of the resource type a loader can load
    std::map<std::type_index, IResourceLoader*> _loaders;

    template<typename ResourceClass, typename ...LoaderArgs>
    IResource* load(LoaderArgs ...args) {
        IResourceLoader* loader = _loaders.find(typeid(ResourceClass))->second; 
        return loader->load(args...);
    }

    void update() {
        for(auto &l: _loaders) {
            l.second->freeUnusedResources();
        }}
};

现在要加载资源,您只需要:

ResourceManager rm;
//Add some loaders to the manager, then load
rm.load<AResource>("path/to/resource", 10, 20);

这样做的重点是确保没有资源被多次加载,并且没有多个加载器实例。

【问题讨论】:

    标签: c++ inheritance c++14 variadic-templates variadic-functions


    【解决方案1】:

    因为用户给了你类型,你可以进行强制转换,不再需要虚拟化:

    class IResourceLoader {
        virtual void freeUnusedResources() = 0;
        virtual ~IResourceLoader() = default;
    };
    

    接下来,我们需要有一个从资源到加载器的类型映射,这样当用户请求资源时,我们就知道要使用哪个加载器。

    template <class Resource>
    struct ResourceToLoaderType;
    
    class ALoader : public IResourceLoader {
        std::vector<IResource*> _loadedResources;
    
        ResourceA load(std::string path);
        ResourceA load(std::string path, int a, int b);
        void freeUnusedResources() override;
    };
    template <>
    struct ResourceToLoaderType<ResourceA>{
        using type = ALoader;
    };
    

    然后,最后:

    template<typename Resource, typename ...LoaderArgs>
    Resource load(LoaderArgs ...args) {
        auto& resource = *_loaders.at(typeid(Resource));
        auto& loader = dynamic_cast<
             typename ResourceToLoaderType<Resource>::type &>(resource);
        return loader.load(args...);
    }
    

    我对引用进行动态转换,以便在您的地图搞砸时抛出。但是,如果您正确地进行插入,那么 dynamic_cast 将始终成功。现在,每个Resource 都可以根据需要定义其load 函数;管理器的用户负责确保参数与加载器一致。好消息是,如果他们传递与load 签名不一致的参数,您会得到编译时失败,而不是运行时。事实上,如果您保持地图一致,则永远不会出现运行时故障。

    其他几个挑剔的人:

    • 使用unique_ptr,经理应该拥有资源
    • 使用完美转发

    【讨论】:

    • 对不起,我弄错了:template&lt;typename Resource, ...&gt; 应该是 template&lt;typename ResourceClass, ...&gt;。我不太明白auto&amp; resource = *_loaders.get():资源现在不应该是IResourceLoader 类型吗?另一件事:您确定存在std::map::get(T key) 吗?我好像没找到。
    • @Gordon 对不起,应该是 at,而不是 get。我也摆脱了显式 typeindex 构造函数,因为没有必要。是的,resourceIResourceLoader 类型,然后你就沮丧了。向下转换是动态的,所以理论上它可能会在运行时失败,但是您的地图是由动态类型索引的,所以向下转换应该总是成功的。一旦你向下转型,你可以ResourceClassload的方法不需要是虚拟的,也不需要在不同资源之间有一致的签名。
    • 我希望我并不清楚这一点:IResourceLoader 不是ResourceClass 的基类,两者之间没有继承关系,它不是资源而是加载器为了那个原因。资源只保存数据,不定义任何load 函数。因此,resource 的类型为 IResourceLoader 的向下转换 dynamic_cast&lt;ResourceClass&amp;&gt;(resource) 不应该工作。我想你的意思是auto&amp; loader = *_loaders.at(...);,而你不小心切换了auto&amp; resourceauto&amp; loader的含义
    • @Gordon 是的,原始编辑中并不清楚。我会再次修复它,虽然这个设计看起来很粗略;过度使用继承。
    • "因为用户给了你类型,你可以执行 cast,并且不需要使它 virtual 任何更多”是我的答案:当我将IResourceManager::load(...) 的模板更改为也采用加载器类型并从 IResourceLoader 中删除 load() 函数时i> 接口,然后我可以将地图中的 IResourceLoader 转换为该类型并调用 load()。另外,我不知道您可以使用using type = ALoader; 存储类的类型。感谢您的努力!
    猜你喜欢
    • 2021-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-15
    • 2013-10-13
    • 2019-03-20
    • 1970-01-01
    • 2018-04-24
    相关资源
    最近更新 更多