C++ 对于std::any-like容器,是否可以转发具有匹配模板类型的函数调用?
我还没有找到一个方法来实现我想要的,但我没有足够的知识来知道这是否是不可能的。我们将不胜感激 我们软件中的主数据容器的行为有点像std::variant或std::any:它有一个提供类型枚举的基类C++ 对于std::any-like容器,是否可以转发具有匹配模板类型的函数调用?,c++,templates,design-patterns,stdbind,stdany,C++,Templates,Design Patterns,Stdbind,Stdany,我还没有找到一个方法来实现我想要的,但我没有足够的知识来知道这是否是不可能的。我们将不胜感激 我们软件中的主数据容器的行为有点像std::variant或std::any:它有一个提供类型枚举的基类BaseContainer。派生实例DataContainer将实际数据保存在类型化的张量成员变量中。因此,一个简化的示例可以归结为如下内容: // One like this for each function. struct ProcessDataWrapper { template <
BaseContainer
。派生实例DataContainer
将实际数据保存在类型化的张量成员变量中。因此,一个简化的示例可以归结为如下内容:
// One like this for each function.
struct ProcessDataWrapper {
template <typename... Args>
static auto run(Args&&... args) {
return processData(std::forward<Args>(args)...);
}
};
template <typename Wrapper>
auto ProcessGeneric(BaseContainer* aContainer) {
switch (vContainer->getType()) {
case DataTypes::INT8:
return Wrapper::run(dynamic_cast<DataContainer<int8_t>>(vContainer)->getData());
// ...
}
// Called as
ProcessGeneric<ProcessDataWrapper>(myContainer);
BaseContainer*vContainer=新的数据容器({1000000});
如果(vContainer->getType()==DataTypes::FLOAT)
常量张量&vTensor=dynamic_cast(vContainer)->getData();
我们有许多基于基础模板类型和维度处理数据的方法:
模板
void processData(常量张量和aTensor,…其他参数…);
问题是,对于我们希望使用BaseContainer
调用的每个方法,如processData()
,我们需要编写一个绑定方法,该方法分解调用processData()
类型版本的可能类型:
void processData(BaseContainer*a容器){
开关(vContainer->getType()){
案例数据类型::INT8:
返回processData(dynamic_cast(vContainer)->getData());
案例数据类型::UINT8:
返回processData(dynamic_cast(vContainer)->getData());
案例数据类型::INT16:
返回processData(dynamic_cast(vContainer)->getData());
案例数据类型::UINT16:
返回processData(dynamic_cast(vContainer)->getData());
...
违约:
抛出(std::runtime_错误(“不支持类型”);
}
}
我的问题是:是否可以创建一个“适配器”方法(在任何已发布的c++版本中),该方法可以接受一个函数(如processData()
)、一个BaseContainer和一个可能的参数列表,并用参数调用此函数的正确模板绑定
我无法动态绑定模板函数,因为在没有模板类型的情况下无法传递名称。然而,模板类型需要基于BaseContainer是动态的。但也许还有其他方法来实现我想做的事情?我对任何解决方案都很好奇,主要是为了扩展我的理解,只要解决方案的复杂性低于编写数百个适配器方法
如果没有其他内容,是否可以使用预处理器宏生成“适配器”方法?如果您愿意为每个类似于
processData
的函数编写一个小包装类,可以执行以下操作:
// One like this for each function.
struct ProcessDataWrapper {
template <typename... Args>
static auto run(Args&&... args) {
return processData(std::forward<Args>(args)...);
}
};
template <typename Wrapper>
auto ProcessGeneric(BaseContainer* aContainer) {
switch (vContainer->getType()) {
case DataTypes::INT8:
return Wrapper::run(dynamic_cast<DataContainer<int8_t>>(vContainer)->getData());
// ...
}
// Called as
ProcessGeneric<ProcessDataWrapper>(myContainer);
//每个函数有一个这样的函数。
结构ProcessDataWrapper{
模板
静态自动运行(Args&&…Args){
返回processData(std::forward(args)…);
}
};
模板
自动处理通用(基本容器*容器){
开关(vContainer->getType()){
案例数据类型::INT8:
返回包装器::run(dynamic_cast(vContainer)->getData();
// ...
}
//称为
过程通用(myContainer);
如果您愿意为每个类似于processData
的函数编写一个小包装类,您可以这样做:
// One like this for each function.
struct ProcessDataWrapper {
template <typename... Args>
static auto run(Args&&... args) {
return processData(std::forward<Args>(args)...);
}
};
template <typename Wrapper>
auto ProcessGeneric(BaseContainer* aContainer) {
switch (vContainer->getType()) {
case DataTypes::INT8:
return Wrapper::run(dynamic_cast<DataContainer<int8_t>>(vContainer)->getData());
// ...
}
// Called as
ProcessGeneric<ProcessDataWrapper>(myContainer);
//每个函数有一个这样的函数。
结构ProcessDataWrapper{
模板
静态自动运行(Args&&…Args){
返回processData(std::forward(args)…);
}
};
模板
自动处理通用(基本容器*容器){
开关(vContainer->getType()){
案例数据类型::INT8:
返回包装器::run(dynamic_cast(vContainer)->getData();
// ...
}
//称为
过程通用(myContainer);
这是可能的,但正如评论所说,它可能值得访问
这里有一个需要c++17的解决方案,每个要包装的函数模板只需要两行代码。您可以使用一个简单的宏来进一步简化包装
其核心思想是拥有一个cast
函数,该函数将数据类型
枚举映射到相应的数据容器
,然后利用c++17折叠表达式将switch语句包装到代码中
这里是cast
函数,因此我们正好有一个位置可以从DataType
映射到活动DataContainer
:
template<DataType t>
constexpr inline decltype(auto) cast(BaseContainer& c) {
if constexpr(t == INT) return static_cast<DataContainer<int>&>(c);
else if constexpr(t == FLOAT) return static_cast<DataContainer<float>&>(c);
... map all other enum values ...
}
//每个功能都需要这样做:
auto-pd=data_type_dispatcher([](auto&c,int-arg){processData(c,arg);})
intmain(){
数据容器f;
数据容器i;
pd(f,2);//打印浮点,2
pd(i,4);//打印int,4
}
完整的例子
要在不支持该类型时引发异常,只需添加在折叠表达式末尾引发的lambda:
([&]{ if(c.GetDataType() == types ) { std::invoke(f, cast<types>(c), args...); return true; } return false; }() || ... || []() -> bool{ throw (std::runtime_error("Type not supported")); }());
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
([&]{if(c.GetDataType()==types){std::invoke(f,cast(c),args…;return true;}return false;}()| |·····································;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
这是可能的,但正如评论所说,它可能值得访问
这里有一个需要c++17的解决方案,每个要包装的函数模板只需要两行代码。您可以使用一个简单的宏来进一步简化包装
其核心思想是拥有一个cast
函数,该函数将数据类型
枚举映射到相应的数据容器
,然后利用c++17折叠表达式将switch语句包装到代码中
这里是cast
函数,因此我们正好有一个位置可以从DataType
映射到活动DataContainer
:
template<DataType t>
constexpr inline decltype(auto) cast(BaseContainer& c) {
if constexpr(t == INT) return static_cast<DataContainer<int>&>(c);
else if constexpr(t == FLOAT) return static_cast<DataContainer<float>&>(c);
... map all other enum values ...
}
//每个功能都需要这样做:
auto pd=data\u type\u dispatcher([](auto&c,int arg){processData(c,ar)
template <typename F>
auto dispatch(BaseContainer& vContainer, F f) {
switch (vContainer.getType()) {
case DataTypes::INT8:
return f(dynamic_cast<DataContainer<int8_t>&>(vContainer).getData());
case DataTypes::UINT8:
return f(dynamic_cast<DataContainer<uint8_t>&>(vContainer).getData());
case DataTypes::INT16:
return f(dynamic_cast<DataContainer<int16_t>&>(vContainer).getData());
case DataTypes::UINT16:
return f(dynamic_cast<DataContainer<uint16_t>&>(vContainer).getData());
...
default:
throw (std::runtime_error("Type not supported"));
}
}
dispatch(vContainer, [](auto* data){ return processData(data); });