C++ [模板] 按类型查找合适的容器

C++ [templates] Finding proper container by type

提问人:Aleix Rius 提问时间:10/1/2023 更新时间:10/1/2023 访问量:47

问:

我正在做一个学习项目,我正在搞砸一些资源加载器,当我在按类型定义容器然后获取它们时遇到卡住时,我试图将其定义为尽可能通用的。

例如,如果我有一个,我该如何根据函数参数定义要选择哪一个?container<TypeA>container<TypeB>

举个虚拟例子:

template <class T>
void DoFoo(T&& t) 
{
  // How to determine which container to use?
  container<TypeA>.Add(t); 
  container<TypeB>.Add(t); 
}

有关实际代码的更多扩展上下文:

class ResourceManager {
    // Resources will be referenced by name --> "Main font", "Background music"...
    using ResourceName = std::string_view;
    // Functor to convert name to hash, in this case using std::hash (good enough).
    using ResourceIDFactory = std::hash<ResourceName>;
    // Hash type or resource ID will be the result of hashing the name: std::hash("Main Font").
    using ResourceID = std::invoke_result<ResourceIDFactory, ResourceName>::type;
        
        // Resource Handle is defined by it's own type trait.
    template<typename T>
    using ResourceMap = std::unordered_map < ResourceID, Resources::Resource<T>::HandleTy>;

public:
    template <typename Resource, typename ... Args>
    auto Load(ResourceName name, Args&& ... args) {
        ResourceID id = GetResoruceID(name);
        if (auto ptr = IsResourceLoaded<Resource>(id))
        {
            return *ptr;
        }

        auto handle = resourceLoader.Load<Resource>(std::forward<Args>(args)...);
                // How do I find the correct containter to add the new handle?

    }

private:

    template<typename T>
    Resources::Resource<T>::HandleTy* IsResourceLoaded(ResourceID id) {
        // this is one of the worst functions I've ever wrote.
        // Problem resides on finding the right container by resource type
        // Might be solvable with some SFINAE and some dark magic. WIP.
        if constexpr (std::is_same_v<T, Font>)
        {
            auto it = fontResources.find(id);
            return (it != fontResources.end()) ? &(*it) : nullptr;
        }
        else if constexpr (std::is_same_v<T, Music>) {
            auto it = musicResources.find(id);
            return (it != musicResources.end()) ? &(*it) : nullptr;
        }
        else
        {
            // this might run unto compilation issue as it not type checked.
            auto it = soundResources.find(id);
            return (it != soundResources.end()) ? &(*it) : nullptr;
        }
    }

    inline ResourceID GetResoruceID(ResourceName name)
    {
        return IdFactory(name);
    }

private:
    Resources::Loader   resourceLoader;
    ResourceIDFactory   IdFactory;
        // Ugly way, could not find a proper way to dynamically generate containers by type
    ResourceMap<Font>   fontResources;
    ResourceMap<Music>      musicResources;
    ResourceMap<Sound>      soundResources;
};

我正在使用 C++ 最新(因此如果需要,我可以使用一些 c++23 功能),但到目前为止,我不知道我应该往哪个方向走。

注意:我没有使用boost,因为我更喜欢从头开始学习。

提前致谢!


我做过的一些实验没有奏效或不喜欢结果:

  • 使用此功能将强制逐个检查所有类型,这些类型不能很好地扩展并使所有功能膨胀。我想放弃这个选项。if constexpr (std::is_same_v<A,B>)

  • 尝试使用静态模板函数按类型生成哈希,并使其具有更高级别的容器:问题是值类型()不是同质的。map<Typehash, Container<T>Container<T>


C++ 模板 、容器类型 特征

评论

1赞 M. Galib Uludag 10/1/2023
也许像这样使用映射并尝试在函数中使用 CRTP 技术,并且类从中派生它们并使用 和map<Typehash, std::any>std::any_cast<T>IsResourceLoadedFontMusicSoundResource<T>std::variantstd::visit
0赞 Aleix Rius 10/1/2023
嗯,所以基本类型擦除,然后投射回去恢复类型,这可能会起作用,对于建议。关于变体,在这种情况下我有点反对,因为它会限制选项,每次我添加新类型时,它都会强制更新变体声明并扩展访问者函子。
0赞 Igor Tandetnik 10/2/2023
在类中:仅声明,无定义。类外:对其他资源类型重复上述步骤。template<typename T> Resources::Resource<T>::HandleTy* IsResourceLoaded(ResourceID id);template<> Resources::Resource<Font>::HandleTy* ResourceManager::IsResourceLoaded(ResourceID id) { /* implement using fontResources */ }

答: 暂无答案