将函数应用于元组中的每个元素,将每个元素转换为类型包中的不同类型,然后作为参数包传递
Apply function to each element in tuple, cast each to a different type in a type pack, then pass as parameter pack
我正在构建一个复杂的可扩展系统。细节并不重要,但我真的很喜欢这个设计,除了这个问题。
我有一个用于某种类型 T 的接口 WithState<T> : Subject
。
有问题的 class 是在 template <typename... StateTypes>
上模板化的。
它持有一个 std::tuple<std::shared_ptr<WithState<StateTypes>>...>>
。
我有一个函数 std::any getStateFor(std::shared_ptr<Subject>)
(其中 WithState<T> : Subject
)。
我还有一个函数void handleStates(StateTypes... states)
(此时不妨取一个元组,哪个更容易)
现在我需要将所有这些部分连接在一起:我需要将元组中的元素向上转换为 shared_ptr<Subject>
,然后按顺序将 getStateFor
应用于每个元素,然后 std::any_cast
结果按 StateTypes...
的顺序,然后将所有这些作为参数包一次转发给 handleStates
。
(在这被标记为 XY 问题之前:更高层次的抽象不关心具体的状态类型,而我想以尽可能多的类型安全来实现较低的部分。到目前为止,这种方法正在寻找适合我的需要)
我可以通过将我的元组转换为向量,对每个元组应用 getStateFor
,然后编写一个应用适当 any_cast
的递归函数来做到这一点,但我仍然没有想法如何将我的结果收集到具有不同类型的元组中。我想知道这是否适用于智能折叠表达式...
这是现有代码的框架:
#include <memory>
#include <tuple>
#include <any>
#include <iostream>
#include <cassert>
#include <functional>
// ignoring references and const for brevity
class Subject {
public:
/* deleted copy assignment and constructor */
// this is here so that the example works
virtual std::any getState() = 0;
};
template <typename T> class WithState : public Subject { };
template <typename... StateTypes>
class StateHandler {
public:
std::tuple<std::shared_ptr<WithState<StateTypes>>...> subjects;
// this one is actually in another class, but it doesn't matter
void handleStates(StateTypes... states);
void handleStatesForSubjects(std::function<std::any (std::shared_ptr<Subject>)> getStateFor) {
// how do I implement this?
}
};
int main() {
struct foo { int a; int b; };
struct WithInt : public WithState<int> {
std::any getState() override { return 17; }
};
struct WithFoo : public WithState<foo> {
std::any getState() override { return foo { 1, 2 }; }
};
StateHandler<int, foo> handler;
handler.subjects = {
std::make_shared<WithInt>(), std::make_shared<WithFoo>() };
handler.handleStatesForSubjects([](auto subj) { return subj->getState(); });
}
但是,这仍然缺少具体类型的 std::any_cast
。
有什么想法吗?
主要问题是你的元组包含 std::shared_ptr<WithState<StateTypes>>...
所以这就是 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::any
(或提供 returns std::any
的非模板化重载)。但是出于这里的目的,转换为 std::any
和返回只是不必要的开销——如果有的话,它是 less 类型安全的。
您的 lambda 参数是 StateTypes&&... withStates
。除了类型本身需要不同之外,&&
不起作用,除非您实际向 lambda 提供临时变量(std::apply
不会这样做),或者如果您通过 [= 进行类型推导23=](这是不同的)。在代码中,lambda 表达式是这样的(为了简单起见,我按值取值,您可以按 [const] 引用取值):
[this](std::shared_ptr<WithState<StateTypes>>... withStatePtrs) {
handleStates(getStateFor<StateTypes>(withStatePtrs)...);
}
您也不需要 dynamic_pointer_cast
从 std::shared_ptr<WithState<T>>
到 std::shared_ptr<Subject>
。
您的元组类型有一个额外的 >
。
编辑:使用更新问题中的代码片段,您会得到:
相同的注意事项仍然适用,您只需在其中放置一个 any_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
);
}
它可能比你想象的更直接,但你只是写下每个元素的操作:调用getStateFor
,然后any_cast
它。对每个可变参数重复 (...
)。您不需要任何 dynamic_pointer_cast
或类似的 - std::shared_ptr<Derived>
可以隐式转换为 std::shared_ptr<Base>
.
我正在构建一个复杂的可扩展系统。细节并不重要,但我真的很喜欢这个设计,除了这个问题。
我有一个用于某种类型 T 的接口 WithState<T> : Subject
。
有问题的 class 是在 template <typename... StateTypes>
上模板化的。
它持有一个 std::tuple<std::shared_ptr<WithState<StateTypes>>...>>
。
我有一个函数 std::any getStateFor(std::shared_ptr<Subject>)
(其中 WithState<T> : Subject
)。
我还有一个函数void handleStates(StateTypes... states)
(此时不妨取一个元组,哪个更容易)
现在我需要将所有这些部分连接在一起:我需要将元组中的元素向上转换为 shared_ptr<Subject>
,然后按顺序将 getStateFor
应用于每个元素,然后 std::any_cast
结果按 StateTypes...
的顺序,然后将所有这些作为参数包一次转发给 handleStates
。
(在这被标记为 XY 问题之前:更高层次的抽象不关心具体的状态类型,而我想以尽可能多的类型安全来实现较低的部分。到目前为止,这种方法正在寻找适合我的需要)
我可以通过将我的元组转换为向量,对每个元组应用 getStateFor
,然后编写一个应用适当 any_cast
的递归函数来做到这一点,但我仍然没有想法如何将我的结果收集到具有不同类型的元组中。我想知道这是否适用于智能折叠表达式...
这是现有代码的框架:
#include <memory>
#include <tuple>
#include <any>
#include <iostream>
#include <cassert>
#include <functional>
// ignoring references and const for brevity
class Subject {
public:
/* deleted copy assignment and constructor */
// this is here so that the example works
virtual std::any getState() = 0;
};
template <typename T> class WithState : public Subject { };
template <typename... StateTypes>
class StateHandler {
public:
std::tuple<std::shared_ptr<WithState<StateTypes>>...> subjects;
// this one is actually in another class, but it doesn't matter
void handleStates(StateTypes... states);
void handleStatesForSubjects(std::function<std::any (std::shared_ptr<Subject>)> getStateFor) {
// how do I implement this?
}
};
int main() {
struct foo { int a; int b; };
struct WithInt : public WithState<int> {
std::any getState() override { return 17; }
};
struct WithFoo : public WithState<foo> {
std::any getState() override { return foo { 1, 2 }; }
};
StateHandler<int, foo> handler;
handler.subjects = {
std::make_shared<WithInt>(), std::make_shared<WithFoo>() };
handler.handleStatesForSubjects([](auto subj) { return subj->getState(); });
}
但是,这仍然缺少具体类型的 std::any_cast
。
有什么想法吗?
主要问题是你的元组包含 std::shared_ptr<WithState<StateTypes>>...
所以这就是 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::any
(或提供 returnsstd::any
的非模板化重载)。但是出于这里的目的,转换为std::any
和返回只是不必要的开销——如果有的话,它是 less 类型安全的。您的 lambda 参数是
StateTypes&&... withStates
。除了类型本身需要不同之外,&&
不起作用,除非您实际向 lambda 提供临时变量(std::apply
不会这样做),或者如果您通过 [= 进行类型推导23=](这是不同的)。在代码中,lambda 表达式是这样的(为了简单起见,我按值取值,您可以按 [const] 引用取值):[this](std::shared_ptr<WithState<StateTypes>>... withStatePtrs) { handleStates(getStateFor<StateTypes>(withStatePtrs)...); }
您也不需要
dynamic_pointer_cast
从std::shared_ptr<WithState<T>>
到std::shared_ptr<Subject>
。您的元组类型有一个额外的
>
。
编辑:使用更新问题中的代码片段,您会得到:
相同的注意事项仍然适用,您只需在其中放置一个 any_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
);
}
它可能比你想象的更直接,但你只是写下每个元素的操作:调用getStateFor
,然后any_cast
它。对每个可变参数重复 (...
)。您不需要任何 dynamic_pointer_cast
或类似的 - std::shared_ptr<Derived>
可以隐式转换为 std::shared_ptr<Base>
.