在 2 个模板函数之间共享模板类型的数据
Sharing data of template type between 2 template functions
也许我高估了 C++ 的元编程能力,但我希望能够创建一个如下所示的 class:
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 的第一个问题,所以请让我放松一下。
谢谢!
如果您只是想调用 c.Get()
并 return 最后存储的任何内容,那是不可能的。
Maybe i'm overestimating metaprogramming capabilities of C++
不要高估,您是在混淆他们不是的东西。
模板(以及 return 类型的函数)是静态的编译时属性。您最后存储在对象中的是动态 属性.
Get
的 return 类型必须在编译时已知,这将需要编译器静态分析程序的其余部分以确定最后调用什么 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?
如果无法将容器作为模板,您需要自己编写代码来动态存储任何类型(查看 "type erasure"),然后
- 有
Get
return 东西 "un-typed" 像 void*
或 std::experimental::any
或 boost::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
类型就有意义了。 )
不完全是您要找的东西,但希望下面的代码能给您一些想法。
#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;
}
类似于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;
}
所以有几个部分。首先,根据类型查找运行时数据。 std::type_index
和 typeid
运算符一起为您解决了这个问题。
第二部分是存储未知类型的数据。 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>()
returns a T*
如果获取失败,则为 nullptr
。它还支持const
访问。
any
基本上是类型安全的 void*
.
也许我高估了 C++ 的元编程能力,但我希望能够创建一个如下所示的 class:
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 的第一个问题,所以请让我放松一下。 谢谢!
如果您只是想调用 c.Get()
并 return 最后存储的任何内容,那是不可能的。
Maybe i'm overestimating metaprogramming capabilities of C++
不要高估,您是在混淆他们不是的东西。
模板(以及 return 类型的函数)是静态的编译时属性。您最后存储在对象中的是动态 属性.
Get
的 return 类型必须在编译时已知,这将需要编译器静态分析程序的其余部分以确定最后调用什么 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?
如果无法将容器作为模板,您需要自己编写代码来动态存储任何类型(查看 "type erasure"),然后
- 有
Get
return 东西 "un-typed" 像void*
或std::experimental::any
或boost::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
类型就有意义了。 )
不完全是您要找的东西,但希望下面的代码能给您一些想法。
#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;
}
类似于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;
}
所以有几个部分。首先,根据类型查找运行时数据。 std::type_index
和 typeid
运算符一起为您解决了这个问题。
第二部分是存储未知类型的数据。 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>()
returns a T*
如果获取失败,则为 nullptr
。它还支持const
访问。
any
基本上是类型安全的 void*
.