在2个模板功能之间共享模板类型的数据

时间:2015-02-18 19:55:33

标签: c++ templates c++11 template-meta-programming

也许我高估了C ++的元编程功能,但我希望能够创建一个如下所示的类:

class Container
{
    template<class T> void Set(T* data)
    {
        //Store data somewhere
    }

    template<class T> T* Get()
    {
        //Return the data saved for T object
    }
};

注意:模板容器本身不是一种选择。

如果C ++允许使用模板字段,那么它本来就非常简单,但我采用了一种丑陋的技术,它涉及函数中的borgs,静态变量以及传递this以区分不同实例的模板化数据

我真的想要一个更优雅的解决方案。

编辑: 这是我目前使用的一个例子(它仍然是一个简化的版本,使点更清楚)

Container.h

#include <functional>
#include <map>
using namespace std;
template<class T>
class FactoryContainer
{
public:
    function<T*()> GetFactoryForContainerInstance(void* container)
    {
        return FactoryContainer<T>::factories[container];
    };

    void SetFactoryForContainerInstance(void* container, function<T*()> factory)
    {
        FactoryContainer<T>::factories[container] = factory;
    };

private:
    static map<void*, function<T*()> > factories;
};

template<class T>
map<void*, function<T*()> > FactoryContainer<T>::factories = map<void*, function<T*()>> ();

class Container
{
public:
    template<class T> void Register(function<T*()> factory)
    {
        SetFactory<T>(factory);
    };
    template<class T> T* Resolve()
    {
        return GetFactory<T>()();
    };

private:
    template<class T>
    function<T*()> GetFactory()
    {
        return FactoryContainer<T>().GetFactoryForContainerInstance(this);
    };

    template<class T>
    void  SetFactory(function<T*()> factory)
    {
        FactoryContainer<T>().SetFactoryForContainerInstance(this, factory);
    };
};

的main.cpp

#include <string>
#include <functional>
#include "Container.h"
using namespace std;
void main()
{
    std::cout << "And thus it has began" << std::endl;

    Container c;
    function<int*()> foo = [] () { return new int(1);};
    c.Register<int>(foo);
    int* i = c.Resolve<int>();
    std::cout << *i  << std::endl; // prints 1
    delete i;
    function<string*()> foo2 = [] () { return new string("I hope it is clear now!");};
    c.Register<string>(foo2);
    string* s = c.Resolve<string>();
    std::cout << *c.Resolve<string>() << std::endl; // prints I hope it is clear now!
    delete s;
    Container c2;
    try
    {
        string* s2 = c2.Resolve<string>();
    }
    catch (...)
    {
        std::cout << "Exception caught, yay!" << std::endl; // exception is intended as there was no registrations on c2,
                                                            //that is why passing void* is needed in implementation
    }

}

我希望能够避免使用FactoryContainer borg。

这是我关于SO的第一个问题,所以请给我一些懈怠。 谢谢!

4 个答案:

答案 0 :(得分:3)

所以有几个部分。首先,根据类型查找运行时数据。 std::type_indextypeid运营商一起解决了这个问题。

第二部分是存储未知类型的数据。 std::experimental::any这样做。

第三部分是将它包起来,所以你不必考虑它。

struct poly_storage {
  std::unordered_map< std::type_index, std::experimental::any > data;
  template<class T, class...Args> void store( Args&&... args ) {
    auto it = data.find(typeid(T));
    if (it == data.end()) {
      data.emplace(
        std::make_pair( typeid(T), T{std::forward<Args>(args)...} )
      );
    } else {
      data[typeid(T)] = T{ std::forward<Args>(args)... };
    }
  }
  template<class T>
  T* get() {
    auto it = data.find(typeid(T));
    if (it == data.end()) return nullptr;
    return std::any_cast<T>(&(it->second));
  }
  template<class T>
  T const* get() const {
    auto it = data.find(typeid(T));
    if (it == data.end()) return nullptr;
    return std::any_cast<T>(&(it->second));
  }
};

poly_storage.store<T>( blah )blah存储在T的密钥下,作为T

如果获取失败,

poly_storage.get<T>()会返回T* nullptr。它还支持const访问。

any基本上是类型安全的void*

答案 1 :(得分:2)

与R.Sahu的答案相似,但使用std::tuple(需要C ++ 14,但可以在C ++ 11中完成)

template<typename ... Args>
class Container
{
public:
    template<class T> void Set(const T& value)
    {
        std::get<T>(data) = value;
    }

    template<class T> const T& Get() const
    {
       return std::get<T>(data);
    }

private:
    std::tuple<Args...> data;
};

int main()
{
   Container<int, float, double> c;
   c.Set(10);
   c.Set(20.125f);
   c.Set(30.7890);

   std::cout << c.Get<int>() << std::endl;
   std::cout << c.Get<float>() << std::endl;
   std::cout << c.Get<double>() << std::endl;
}

Live example

答案 2 :(得分:1)

如果你只是想打电话给c.Get()并让它返回最后存储的内容,那就不可能了。

  

也许我高估了C ++的元编程功能

不要过高估计,你会因为他们不喜欢的东西而混淆他们。

模板(以及函数的返回类型)是静态的编译时属性。最后存储在对象中的是动态属性。

必须在编译时知道返回类型Get,这需要编译器静态分析程序的其余部分,以确定对Set的最后一次调用是什么以及类型是什么它存储。在一般情况下,这是不可能的,对Set的调用可能位于编译器不可见的不同翻译单元中,或者您可以这样做:

Container c;
int i = 1;
std::string s = "two";
if (rand() % 2)
  c.Set(&i);
else
  c.Set(&s);
auto res = c.Get();  // What is the type here?

如果让容器成为模板不是一个选项,您需要自己编写代码以动态存储任何类型(查看&#34;类型擦除&#34;)然后

  • Get返回一些&#34; un-typed&#34;例如void*std::experimental::anyboost::any,并要求用户转回原始类型;或
  • 要求用户致电Get<int>(),以便他们指定类型并让容器进行投射。

例如,使用std::experimental::any执行类型擦除和转换如下所示:

class Container
{
    std::experimental::any m_data;

public:
    template<class T> void Set(T* data)
    {
        m_data = std::experimental::any(data);
    }

    template<class T> T* Get()
    {
        return std::experimental::any_cast<T*>(m_data)
    }
};

Container c;
int i = 1;
c.Set(&i);
int* pi = c.Get<int>();
std::string s = "two";
c.Set(&s);
std::string* ps = c.Get<std::string*>();
// this will return a null pointer:
pi = c.Get<int*>();

(N.B。这并没有比直接使用any更有优势,但也许你想添加其他功能,以便Container类型有意义。)

答案 3 :(得分:1)

不是你想要的,但希望下面的代码能给你一些想法。

#include <iostream>

class Container
{
   public:

    template<class T> void Set(T data)
    {
       static_cast<Datum<T>&>(data_).datum = data;
    }

    template<class T> T Get() const
    {
       return static_cast<Datum<T> const&>(data_).datum;
    }

   private:

    template <typename T>
    struct Datum
    {
       T datum;
    };

    struct Data : Datum<int>,
                  Datum<float>,
                  Datum<double>
   {
   };

    Data data_;
};

int main()
{
   Container c;
   c.Set(10);
   c.Set(20.125f);
   c.Set(30.7890);

   std::cout << c.Get<int>() << std::endl;
   std::cout << c.Get<float>() << std::endl;
   std::cout << c.Get<double>() << std::endl;

   return 0;
}