C++ 将可变数量的参数转换为显式数量

C++ 将可变数量的参数转换为显式数量,c++,inheritance,c++14,variadic-templates,variadic-functions,C++,Inheritance,C++14,Variadic Templates,Variadic Functions,我试图实现一个资源管理器,它管理特定的加载程序,并提供一个load()函数,该函数重定向到加载程序的各个load()函数。管理器还执行某种类型的垃圾收集,例如,通过调用每个加载程序的freeUnusedResources()方法卸载未使用的资源 问题: 将可变数量的参数转换为显式数量。 我希望每个加载程序实现能够提供具有固定数量参数的不同加载函数,而接口/基类IResourceLoader可以处理可变数量,这是ResourceManager的单个load()函数所需要的 比如: class IR

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

问题:
将可变数量的参数转换为显式数量。
我希望每个加载程序实现能够提供具有固定数量参数的不同加载函数,而接口/基类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();
        }}
};
类资源//资源只保存数据
类AResource:publicIResource{};
类IResourceLoader{//Loader加载资源
虚拟IResource*加载(…);
//或
模板
IResource*加载(Args…Args);
虚拟void freeunsedresources()=0;
};
类ALoader:公共IResourceLoader{
std::向量加载资源;
IResource*加载(std::string path);/“正在实现”IResourceLoader::加载(…)
IResource*加载(std::string path,int a,int b);/“实现”IResourceLoader::加载(…)
作废freeUnusedResources()覆盖;
};
班级资源经理{
//加载程序可以加载的资源类型的映射
标准::地图装载机;
模板
IResource*加载(LoaderArgs…args){
IResourceLoader*loader=\u loaders.find(typeid(ResourceClass))->秒;
返回加载程序->加载(参数…);
}
无效更新(){
用于(自动和l:\装载机){
l、 第二->释放未使用的资源();
}}
};
现在要加载资源,您只需要:

ResourceManager rm;
//Add some loaders to the manager, then load
rm.load<AResource>("path/to/resource", 10, 20);
ResourceManager-rm;
//向管理器添加一些加载程序,然后加载
rm.load(“路径/目标/资源”,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;
};
模板
结构ResourceToLoaderType;
类ALoader:公共IResourceLoader{
std::向量加载资源;
ResourceA加载(标准::字符串路径);
ResourceA加载(标准::字符串路径,int a,int b);
作废freeUnusedResources()覆盖;
};
模板
结构ResourceToLoaderType{
使用类型=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...);
}
模板
资源加载(LoaderArgs…args){
auto&resource=*_loaders.at(typeid(resource));
自动和装载机=动态<
typename ResourceToLoaderType::type&>(资源);
返回加载器。加载(args…);
}
我在参考上做了一个动态转换,这样如果你的地图被弄乱了,它就会抛出。但是如果插入正确,那么动态的_cast将始终成功。现在,每个
资源
都可以根据需要定义其
加载
功能;管理器的用户负责确保参数与加载程序一致。很好的一点是,如果它们传递的参数与
load
签名不一致,则会出现编译时故障,而不是运行时故障。事实上,如果保持映射一致,就永远不会出现运行时失败

还有一些其他的挑剔之处:

  • 使用
    unique\u ptr
    ,经理应拥有资源
  • 使用完美转发

对不起,我犯了一个错误:
模板
应该是
模板
。我不太理解
auto&resource=*\u loaders.get()
:资源现在不应该是
IResourceLoader
类型吗?另一件事:你确定存在一个
std::map::get(T键)
?“我好像找不到它。@戈登,对不起,应该是
,而不是
得到
。我还去掉了显式类型索引构造函数,因为这不是必需的。是的,
resource
的类型为
IResourceLoader
,然后您可以向下播放。向下广播是动态的,因此理论上它可能在运行时失败,但您的地图是由动态类型索引的,因此向下广播应该总是成功的。下载后,您可以使用
ResourceClass
的方法,并且
load
不需要是虚拟的,也不需要在不同的资源之间具有一致的签名。我希望我没有不清楚这一点:IResourceLoader不是
ResourceClass
的基类,两者之间没有继承关系,它不是一个资源,而是一个加载程序。资源仅保存数据,不定义任何
load
函数。因此,下行
动态\u-cast(资源)
其中
资源
属于IResourceLoader类型,不应工作。我想你的意思是
auto&loader=*\u loaders.at(…)auto&resource
auto&loader
@Gordon的意思切换了。是的,原来的编辑不清楚。我会再把它修好的,这个设计看起来很粗略,虽然tbh;过度使用继承。“因为用户给了您类型,所以您可以执行强制转换,而不需要再将其虚拟化”是我的答案:当我将
IResourceManager::load(…)
的模板更改为也采用加载程序的类型并从IResourceLoader接口中删除load()函数时,然后我可以将IResourceLoader从映射转换为该类型并调用load()。另外,我不知道可以使用type=ALoader使用
存储类的类型。谢谢你的努力!