C++模板std::tuple到VOID*并返回 [英] C++ templates std::tuple to void* and back

查看:0
本文介绍了C++模板std::tuple到VOID*并返回的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用C++11和各种模板创建一个资源管理器。问题是如何将std::tuple存储到集合并将其取回?在本例中,我尝试将其存储为VOID*(在此尽量不使用Boost::Any)。每次我转换回std::tuple时,我得到的强制转换的元组与从参数创建的元组相同(CurrentArgs==StoredArgs)。我认为下面的代码解释了一切。

http://ideone.com/h3yzvy

#include <memory>
#include <typeindex>
#include <iostream>
#include <string>
#include <vector>
#include <map>

typedef std::multimap<std::type_index, void*> Object;
typedef std::map<Object, std::shared_ptr<void>> ObjectCollection;

Object object;
ObjectCollection objectCollection;

template<typename T, typename... Args>
T* getResource(Args&& ... args)
{
    // Creating tuple from the arguments
    std::tuple<Args...> currentArgs(std::forward<Args>(args)...);
    // Getting object type info
    std::type_index type = { typeid(T) };

    // Getting all objects from the collection that are of the same type
    auto range = object.equal_range(type);

    for (auto it = range.first; it != range.second; ++it)
    {
        // it->second is a void* Since we are iterating through
        // the the collection of the same type I'm trying to cast
        // back. Object construct parameters should be the same 
        // (in this example: const std::string &fileName)
        auto storedArgs = *static_cast<std::tuple<Args...>*>(it->second);

        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        // Problem is here. currentArgs and storedArgs are always equal :/
        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        // Return the object from the collection if current arguments and
        // arguments from the collection are the same
        if (currentArgs == storedArgs)
        {
            std::cout << "Found... returning..." << std::endl;
            // found... return...
            return static_cast<T*>(objectCollection[object].get());
        }
    }

    // Object with the same arguments were not found
    // Adding to collection and return
    std::cout << "Adding to collection..." << std::endl;
    object.emplace(type, &currentArgs);
    objectCollection[object] = std::make_shared<T>(std::forward<Args>(args)...);
    return static_cast<T*>(objectCollection[object].get());
}

class Resource
{
public:
    virtual ~Resource() = default;

    template<typename T, typename... Args>
    static T* get(Args&& ... args)
    {
        return getResource<T>(std::forward<Args>(args)...);
    }
};

class Image
{
public:
    Image(const std::string &fileName)
    {
        std::cout << "Loading image " << fileName.c_str() << std::endl;
    }

    ~Image(){};
};

int main()
{
    auto image1 = Resource::get<Image>("aaa.jpg");
    auto image2 = Resource::get<Image>("bbb.jpg");
    auto image3 = Resource::get<Image>("aaa.jpg");
    getchar();
}

编辑

感谢大家的投入。如果有人关心我的最终资源.h看起来像这样,并且工作完美:

#pragma once

#include <memory>
#include <map>

template<class T, class...Args>
std::map<std::tuple<Args...>, std::shared_ptr<T>>& getCache()
{
    static std::map<std::tuple<Args...>, std::shared_ptr<T>> cache; // only run once
    return cache;
}

template<typename T, typename... Args>
std::shared_ptr<T> getResource(Args&& ... args)
{
    // std::decay_t should be used
    auto& cache = getCache<T, std::decay_t<Args>...>();

    // Creating tuple from the arguments
    auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);

    // Search for object in the cache
    auto it = cache.find(arguments);

    if (it != cache.end())
    {
        // Found. Return.
        return it->second;
    }

    // Not found. Add to cache.
    auto object = std::make_shared<T>(std::forward<Args>(args)...);
    cache.emplace(std::make_pair(std::move(arguments), object));
    return object;
}

class Resource
{
public:
    virtual ~Resource() = default;

    template<typename T, typename... Args>
    static std::shared_ptr<T> get(Args&& ... args)
    {
        return getResource<T>(std::forward<Args>(args)...);
    }
};

推荐答案

为什么不针对每个类型和参数使用一个函数本地映射? 由于您已经按照以下两个条件过滤数据,因此可以简化代码:

#include <iostream>
#include <math.h>

using namespace std; 

#include <memory>
#include <typeindex>
#include <iostream>
#include <string>
#include <vector>
#include <map>

template<typename T, typename... Args>
std::shared_ptr<T> getResource(Args&& ... args)
{
    static std::map<std::tuple<Args...>, std::shared_ptr<T>> objectCollection;

    // Creating tuple from the arguments
    std::tuple<Args...> currentArgs(std::forward<Args>(args)...);

    //Search for object in map
    auto objectIter = objectCollection.find(currentArgs);

    if(objectIter != objectCollection.end())
    {
        std::cout << "Found... returning..." << std::endl;
        return objectIter->second;
    }

    std::shared_ptr<T> newObject(new T(args...));
    std::cout << "Adding to collection..." << std::endl;
    objectCollection.insert(std::pair<std::tuple<Args...>, std::shared_ptr<T>>(currentArgs, newObject));
    return newObject;
}

class Resource
{
public:
    virtual ~Resource() = default;

    template<typename T, typename... Args>
    static std::shared_ptr<T> get(Args&& ... args)
    {
        return getResource<T>(std::forward<Args>(args)...);
    }
};

class Image
{
public:
    Image(const std::string &fileName)
    {
        std::cout << "Loading image " << fileName.c_str() << std::endl;
    }

    ~Image() {};
};

int main()
{
    auto image1 = Resource::get<Image>("aaa.jpg");
    auto image2 = Resource::get<Image>("bbb.jpg");
    auto image3 = Resource::get<Image>("aaa.jpg");
    getchar();
}

编辑:我还将代码更改为始终使用SHARED_PTR。

这篇关于C++模板std::tuple到VOID*并返回的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆