C++ 将函数应用于元组中的每个元素,将每个元素强制转换为类型包中的不同类型,然后作为参数包传递
我正在构建一个复杂的可扩展系统。细节并不重要,但我真的很喜欢这个设计,除了这个问题 对于某些类型的T,我有一个带有state:Subject的接口C++ 将函数应用于元组中的每个元素,将每个元素强制转换为类型包中的不同类型,然后作为参数包传递,c++,templates,c++17,variadic-functions,C++,Templates,C++17,Variadic Functions,我正在构建一个复杂的可扩展系统。细节并不重要,但我真的很喜欢这个设计,除了这个问题 对于某些类型的T,我有一个带有state:Subject的接口 有问题的类是在模板上模板化的 它包含一个std::tuple> 我有一个函数std::any getStateFor(std::shared_ptr)(其中WithState:Subject) 我还有一个函数void handleStates(StateTypes…states)(此时不妨使用一个元组,只要更简单) 现在我需要将所有这些部分插在一起:
有问题的类是在模板
上模板化的
它包含一个std::tuple>
我有一个函数std::any getStateFor(std::shared_ptr)
(其中WithState:Subject
)
我还有一个函数void handleStates(StateTypes…states)
(此时不妨使用一个元组,只要更简单)
现在我需要将所有这些部分插在一起:我需要将元组中的元素向上转换为shared\u ptr
,然后按顺序将getStateFor
应用于这些元素中的每个元素,并按StateTypes…
的顺序对结果进行转换,然后将所有这些作为参数包一次转发到handleStates
(在这之前被标记为XY问题:更高级别的抽象不关心具体的状态类型,而我希望实现具有尽可能多的类型安全性的较低部分。到目前为止,这种方法看起来很适合我的需要)
我可能可以通过将我的元组转换为向量来实现这一点,在每个元组上应用getStateFor
,然后编写一个递归函数来应用适当的any\u cast
,但是我仍然不知道如何将结果收集到不同类型的元组中。我想知道这是否适用于智能折叠表达式
以下是现有代码的框架:
#包括
#包括
#包括
但是,这仍然缺少对具体类型的std::any_cast
s
有什么想法吗?主要问题是元组包含std::shared\u ptr…
,因此apply
将尝试使用调用给定的lambda,但lambda只接受StateTypes&&…
为了让它工作,还有一些变化,但首先是工作:
- 我将
getStateFor
更改为一个模板函数,您可以在其中指定所需的类型:
template<class StateType>
StateType getStateFor(std::shared_ptr<Subject> s)
{
if (auto withState = std::dynamic_pointer_cast<WithState<StateType>>(s))
return withState->getValue();
throw "AHHH";
}
- 从
std::shared_ptr
转到std::shared_ptr
也不需要dynamic_指针\u cast
- 您的元组类型有一个额外的
编辑:使用更新问题中的代码段,您可以得到以下结果:
同样的考虑仍然适用,您只需在其中添加一个any\u cast
:
void handleStatesForSubjects(std::function<std::any(std::shared_ptr<Subject>)> getStateFor)
{
std::apply(
[this, getStateFor](std::shared_ptr<WithState<StateTypes>>... withStates) {
handleStates(std::any_cast<StateTypes>(getStateFor(withStates))...);
},
subjects
);
}
void handleStatesForSubjects(std::function getStateFor)
{
标准::应用(
[this,getStateFor](std::shared_ptr…with states){
HandleState(标准::任意类型(getStateFor(withStates))…);
},
学科
);
}
它可能比您预期的更简单,但您只需写下每个元素的操作:调用getStateFor
,然后调用any\u cast
。对每个可变参数重复(…
)。您不需要任何dynamic\u pointer\u cast
或类似的-std::shared\u ptr
可以隐式转换为std::shared\u ptr
,如果您提供了相关类型的框架,我们将更容易使用它。目前,任何想回答这个问题的人都必须手工制作。我们都是程序员,所以如果您想交流代码的功能,只需显示代码即可。不要试图解释它。“我需要将元组中的元素向上转换为shared_ptr,然后按顺序将getStateFor应用于这些元素中的每一个,然后按StateTypes的顺序将结果转换为std::any_…,然后将所有这些作为参数包一次转发给handleStates。”为什么需要使用迂回的方法来实现这一点?如果您有StateTypes
,那么您就知道这些东西包含哪些类型。那么为什么要转换为基类呢?如果它们已转换为基类,为什么不将它们转换回具有StateTypes
的派生类呢?当你知道这些类型时,为什么要使用这种任何方法呢?@nicolasbolas我添加了一个代码框架,这可能会有所帮助。关键是我从一个更高、更动态的抽象级别获得了handleState
函数(这也是从另一个级别的较低级别获得的)。整个设计需要一篇论文,我还没有写。但是C#中一个更简单的版本在生产环境中工作,所以我知道我走在了正确的轨道上
在b
之后缺少分号。我建议(或其他在线编译器)快速检查代码是否已编译。任何试图回答此问题的人都可能会将其粘贴到其中作为第一步,因此如果没有错误,您的问题将有一个良好的开端。@MaxLanghof感谢您的提示,我编辑了代码并添加了一个godbolt链接。遗憾的是,getStateFor
来自更高级别的抽象,它动态地管理许多具有部分重叠类型的StateHandler
s。所以我不能给它做模板。我仍然很感激你对我的方法的见解。@Dracam从我所知道的情况来看,这不应该是个问题。这只是一个更无意义的铸造。正在将此应用于您的代码示例。@Dracam我仍然对std::any
持怀疑态度,但我想它不会比std::dynamic\u pointer\u cast
更昂贵(这就是std::any
最初的工作原理,这就是为什么这是一个奇怪的组合)。请注意,孤立地说,这个问题可以在没有任何std::any
、commonSubject
基类或其他形式的类型擦除的情况下解决(因此,如果相关的话,性能会好得多):注意:类型和值扩展的混合是有效的,因为您详细说明了lambda的参数列表(你不再推断了)
void handleStatesForSubjects(std::function<std::any(std::shared_ptr<Subject>)> getStateFor)
{
std::apply(
[this, getStateFor](std::shared_ptr<WithState<StateTypes>>... withStates) {
handleStates(std::any_cast<StateTypes>(getStateFor(withStates))...);
},
subjects
);
}