将可变数量的参数转换为显式数量
Converting a variable amount of arguments into an explicit amount
我正在尝试实现一个资源管理器来管理特定的加载器并提供一个 load() 函数,该函数重定向到加载器的各个 load() 函数。管理器还执行某种垃圾收集,例如通过调用每个加载程序的 freeUnusedResources() 方法来卸载未使用的资源。
问题:
将可变数量的参数转换为显式数量。
我希望每个加载器实现能够提供具有固定数量参数的不同加载函数,而 interface/base class 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...);
}
我对一个引用进行了动态转换,这样当您的地图被弄乱时它就会抛出。但是,如果您正确地进行了插入,那么 dynamic_cast 将始终成功。现在,每个 Resource
都可以随心所欲地定义其 load
功能;管理器的用户负责确保参数与加载程序一致。好的是,如果他们传递的参数与 load
签名不一致,你会得到一个编译时失败,而不是 运行 时间。事实上,如果您保持地图一致,您永远不会遇到 运行 次失败。
另外几个吹毛求疵的人:
- 使用
unique_ptr
,经理应该拥有资源
- 使用完美转发
我正在尝试实现一个资源管理器来管理特定的加载器并提供一个 load() 函数,该函数重定向到加载器的各个 load() 函数。管理器还执行某种垃圾收集,例如通过调用每个加载程序的 freeUnusedResources() 方法来卸载未使用的资源。
问题:
将可变数量的参数转换为显式数量。
我希望每个加载器实现能够提供具有固定数量参数的不同加载函数,而 interface/base class IResourceLoader 可以处理可变数量,即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);
这样做的重点是确保没有资源被加载超过一次,并且加载程序没有多个实例。
因为用户给了你类型,你可以进行强制转换,不需要再虚化了:
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
,经理应该拥有资源 - 使用完美转发