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

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

所以像:

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

这样做的全部目的是确保没有资源被加载超过一次,并且确保加载器没有多个实例。

最佳答案

由于用户为您提供了类型,因此您可以执行强制类型转换,而不再需要使其虚拟化:

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

我对引用进行动态投射,以便在您的 map 弄乱时抛出。但是,如果正确执行插入操作,则dynamic_cast将始终成功。现在,每个Resource都可以根据需要定义其load函数。管理员的用户有责任确保参数与加载程序一致。令人高兴的是,如果它们传递与load签名不一致的参数,则会导致编译时失败,而不是运行时失败。确实,如果保持映射一致,就永远不会出现运行时失败。

其他一些nitpicks:
  • 使用unique_ptr,管理者应拥有
  • 资源
  • 使用完美转发
  • 关于c++ - 将可变数量的参数转换为显式数量,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46121998/

    10-13 08:13