我有一个包含节点的场景图。每个节点都有一个转换和几个组件。我有一个二维场景编辑器,目前正在执行以下操作以选择要编辑的组件:
//Within the node class I can grab a list of components
template<typename ComponentType = Component>
std::vector<SafeComponent<ComponentType>> components(bool exactType = true) const {
std::lock_guard<std::recursive_mutex> guard(lock);
std::vector<SafeComponent<ComponentType>> matchingComponents;
if (exactType) {
for (auto&& currentComponent : childComponents) {
if (typeid(currentComponent.get()) == typeid(ComponentType)) {
matchingComponents.push_back(SafeComponent<ComponentType>(shared_from_this(), std::static_pointer_cast<ComponentType>(currentComponent)));
}
}
} else {
for (auto&& currentComponent : childComponents) {
auto castComponent = std::dynamic_pointer_cast<ComponentType>(currentComponent);
if (castComponent) {
matchingComponents.push_back(SafeComponent<ComponentType>(shared_from_this(), castComponent));
}
}
}
return matchingComponents;
}
然后在我的编辑器代码中我有以下
auto componentList = controls->elementToEdit->components(false);
for (auto&& component : componentList) {
std::cout << typeid(*component.self().get()).name() << std::endl;
if (typeid(*component.self().get()) == typeid(MV::Scene::Sprite)) {
auto button = makeButton(grid, *panel.resources().textLibrary, *panel.resources().mouse, MV::guid("EditSprite"), buttonSize, MV::toWide("S: " + component->id()));
button->onAccept.connect("click", [&,component](std::shared_ptr<MV::Scene::Clickable>) {
panel.loadPanel<SelectedRectangleEditorPanel>(std::make_shared<EditableRectangle>(component.cast<MV::Scene::Sprite>(), panel.content(), panel.resources().mouse));
});
}else if (typeid(*component.self().get()) == typeid(MV::Scene::Grid)) {
auto button = makeButton(grid, *panel.resources().textLibrary, *panel.resources().mouse, MV::guid("EditGrid"), buttonSize, MV::toWide("G: " + component->id()));
button->onAccept.connect("click", [&, component](std::shared_ptr<MV::Scene::Clickable>) {
panel.loadPanel<SelectedGridEditorPanel>(std::make_shared<EditableGrid>(component.cast<MV::Scene::Grid>(), panel.content(), panel.resources().mouse));
});
}else if (typeid(*component.self().get()) == typeid(MV::Scene::Emitter)) {
auto button = makeButton(grid, *panel.resources().textLibrary, *panel.resources().mouse, MV::guid("EditEmitter"), buttonSize, MV::toWide("E: " + component->id()));
button->onAccept.connect("click", [&, component](std::shared_ptr<MV::Scene::Clickable>) {
panel.loadPanel<SelectedEmitterEditorPanel>(std::make_shared<EditableEmitter>(component.cast<MV::Scene::Emitter>(), panel.content(), panel.resources().mouse));
});
}
}
我想利用访客模式,但我不想通过创建一个“访问者”来密封我的课程,该访问者必须针对每种潜在类型的组件进行修改。值得庆幸的是,我找到了一个很酷的概念性解决方案。
https://ciaranm.wordpress.com/2010/07/15/generic-lambda-visitors-or-writing-haskell-in-c0x-part-4/
如果我可以修改我的Node :: components方法返回
std::vector<OneOf<[ParameterPackPseudoCode]>>
我可以这样写:
auto componentList = controls->elementToEdit->components<Sprite, Grid, Emitter>();
for (auto&& component : componentList) {
where(component,
[&](const SafeComponent<Sprite> &sprite){
auto button = makeButton(grid, *panel.resources().textLibrary, *panel.resources().mouse, MV::guid("EditSprite"), buttonSize, MV::toWide("S: " + component->id()));
button->onAccept.connect("click", [&,component](std::shared_ptr<MV::Scene::Clickable>) {
panel.loadPanel<SelectedRectangleEditorPanel>(std::make_shared<EditableRectangle>(sprite, panel.content(), panel.resources().mouse));
});
},
[&](const SafeComponent<Grid> &grid){
auto button = makeButton(grid, *panel.resources().textLibrary, *panel.resources().mouse, MV::guid("EditGrid"), buttonSize, MV::toWide("G: " + component->id()));
button->onAccept.connect("click", [&, component](std::shared_ptr<MV::Scene::Clickable>) {
panel.loadPanel<SelectedGridEditorPanel>(std::make_shared<EditableGrid>(grid, panel.content(), panel.resources().mouse));
});
},
[&](const SafeComponent<Emitter> &emitter){
auto button = makeButton(grid, *panel.resources().textLibrary, *panel.resources().mouse, MV::guid("EditEmitter"), buttonSize, MV::toWide("E: " + component->id()));
button->onAccept.connect("click", [&, component](std::shared_ptr<MV::Scene::Clickable>) {
panel.loadPanel<SelectedEmitterEditorPanel>(std::make_shared<EditableEmitter>(emitter, panel.content(), panel.resources().mouse));
});
}
);
}
我的问题是,我想写这样的东西:
myNode->components<Sprite, Grid, Emitter>();
然后回来:
std::vector<OneOf<SafeComponent<Sprite>, SafeComponent<Grid>, SafeComponent<Emitter>>>
这有两个问题,我真的不知道要解决的语法或方法:
1)我需要能够在:
中转换Sprite,Grid,Emittertemplate<typename ...ComponentList> Node::components
向
OneOf<SafeComponent<Sprite>, SafeComponent<Grid>, SafeComponent<Emitter>>
2)我需要能够对参数包中的每个项运行typeid(... ComponentList),以确保它在迭代我在Node内部的组件列表中时实际匹配我正在寻找的底层类型以便push_back只有匹配的组件向量中的所需元素。
如果你看看我目前如何检查单一类型,这很简单,但我真正需要的是:
if(TypeMatchesAnyOf<...ComponentList>(currentComponent)){
matchingComponents.push_back(SafeComponent<ComponentType>(shared_from_this(), std::static_pointer_cast<TypeMatched<...ComponentList>(currentComponent)::type>(currentComponent)));
}
TypeMatchesAnyOf和TypeMatched :: type是我可以看到积分的两个组件。
答案 0 :(得分:1)
经过相当多的实验,我能够解决我的问题。基本上我在这里做的是基于可变参数模板参数过滤对象的向量。这是混合运行时和编译时类型检查的挑战。
希望这有助于其他人,因为我相信它是一种有用的模式,可以按类型过滤组件并应用访问者,而无需将访问者的任何知识都添加到节点类中。这基本上解决了访问者模式的问题,需要静态维护的双重调度类。
相关包含/使用std(不要在标题中使用std,我只在此处包含它,因为我想包含所有的ideone链接,这样你就可以复制/粘贴这个答案的每个部分来创建完整的程序) :
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <typeinfo>
#include <typeindex>
#include <type_traits>
#include <utility>
#include <tuple>
using namespace std;
OneOf和我所依赖的库(https://ciaranm.wordpress.com/2010/07/15/generic-lambda-visitors-or-writing-haskell-in-c0x-part-4/):
struct UnknownTypeForOneOf;
template <typename Want_, typename... Types_>
struct SelectOneOfType;
template <typename Want_>
struct SelectOneOfType<Want_>
{
typedef UnknownTypeForOneOf Type;
};
template <typename Want_, typename Try_, typename... Rest_>
struct SelectOneOfType<Want_, Try_, Rest_...>
{
typedef typename std::conditional<
std::is_same<Want_, Try_>::value,
Try_,
typename SelectOneOfType<Want_, Rest_...>::Type
>::type Type;
};
template <typename Type_>
struct ParameterTypes;
template <typename C_, typename R_, typename P_>
struct ParameterTypes<R_ (C_::*)(P_)>
{
typedef P_ FirstParameterType;
typedef R_ ReturnType;
};
template <typename C_, typename R_, typename P_>
struct ParameterTypes<R_ (C_::*)(P_) const>
{
typedef P_ FirstParameterType;
typedef R_ ReturnType;
};
template <typename Lambda_>
struct LambdaParameterTypes
{
typedef typename ParameterTypes<decltype(&Lambda_::operator())>::FirstParameterType FirstParameterType;
typedef typename ParameterTypes<decltype(&Lambda_::operator())>::ReturnType ReturnType;
};
template <typename Type_>
struct OneOfVisitorVisit
{
virtual void visit(Type_ &) = 0;
};
template <typename... Types_>
struct OneOfVisitor :
OneOfVisitorVisit<Types_>...
{
};
template <typename Visitor_, typename Underlying_, typename Result_, typename... Types_>
struct OneOfVisitorWrapperVisit;
template <typename Visitor_, typename Underlying_, typename Result_>
struct OneOfVisitorWrapperVisit<Visitor_, Underlying_, Result_> :
Visitor_
{
Underlying_ & underlying;
std::function<Result_ ()> execute;
OneOfVisitorWrapperVisit(Underlying_ & u) :
underlying(u)
{
}
};
template <typename Visitor_, typename Underlying_, typename Result_, typename Type_, typename... Rest_>
struct OneOfVisitorWrapperVisit<Visitor_, Underlying_, Result_, Type_, Rest_...> :
OneOfVisitorWrapperVisit<Visitor_, Underlying_, Result_, Rest_...>
{
OneOfVisitorWrapperVisit(Underlying_ & u) :
OneOfVisitorWrapperVisit<Visitor_, Underlying_, Result_, Rest_...>(u)
{
}
Result_ visit_returning(Type_ & t)
{
return this->underlying.visit(t);
}
virtual void visit(Type_ & t)
{
this->execute = std::bind(&OneOfVisitorWrapperVisit::visit_returning, this, std::ref(t));
}
};
template <typename Underlying_, typename Result_, typename... Types_>
struct OneOfVisitorWrapper :
OneOfVisitorWrapperVisit<OneOfVisitor<Types_...>, Underlying_, Result_, Types_...>
{
OneOfVisitorWrapper(Underlying_ & u) :
OneOfVisitorWrapperVisit<OneOfVisitor<Types_...>, Underlying_, Result_, Types_...>(u)
{
}
};
template <typename... Types_>
struct OneOfValueBase
{
virtual ~OneOfValueBase() = 0;
virtual void accept(OneOfVisitor<Types_...> &) = 0;
virtual void accept(OneOfVisitor<const Types_...> &) const = 0;
};
template <typename... Types_>
OneOfValueBase<Types_...>::~OneOfValueBase() = default;
template <typename Type_, typename... Types_>
struct OneOfValue :
OneOfValueBase<Types_...>
{
Type_ value;
OneOfValue(const Type_ & type) :
value(type)
{
}
virtual void accept(OneOfVisitor<Types_...> & visitor)
{
static_cast<OneOfVisitorVisit<Type_> &>(visitor).visit(value);
}
virtual void accept(OneOfVisitor<const Types_...> & visitor) const
{
static_cast<OneOfVisitorVisit<const Type_> &>(visitor).visit(value);
}
};
template <typename... Types_>
class OneOf
{
private:
std::unique_ptr<OneOfValueBase<Types_...> > _value;
public:
template <typename Type_>
OneOf(const Type_ & value) :
_value(new OneOfValue<typename SelectOneOfType<Type_, Types_...>::Type, Types_...>{value})
{
}
OneOf(const OneOf & other) = delete;
OneOf(OneOf && other) :
_value(std::move(other._value))
{
}
template <typename Type_>
OneOf & operator= (const Type_ & value)
{
_value.reset(new OneOfValue<typename SelectOneOfType<Type_, Types_...>::Type, Types_...>{value});
return *this;
}
OneOf & operator= (const OneOf & other) = delete;
OneOf & operator= (OneOf && other)
{
_value = std::move(other._value);
return *this;
}
OneOfValueBase<Types_...> & value()
{
return *_value;
}
const OneOfValueBase<Types_...> & value() const
{
return *_value;
}
};
template <typename Visitor_, typename Result_, typename OneOf_>
struct OneOfVisitorWrapperTypeFinder;
template <typename Visitor_, typename Result_, typename... Types_>
struct OneOfVisitorWrapperTypeFinder<Visitor_, Result_, const OneOf<Types_...> &>
{
typedef OneOfVisitorWrapper<Visitor_, Result_, const Types_...> Type;
};
template <typename Visitor_, typename Result_, typename... Types_>
struct OneOfVisitorWrapperTypeFinder<Visitor_, Result_, OneOf<Types_...> &>
{
typedef OneOfVisitorWrapper<Visitor_, Result_, Types_...> Type;
};
template <typename Result_, typename OneOf_, typename Visitor_>
Result_
accept_returning(OneOf_ && one_of, Visitor_ && visitor)
{
typename OneOfVisitorWrapperTypeFinder<Visitor_, Result_, OneOf_>::Type visitor_wrapper(visitor);
one_of.value().accept(visitor_wrapper);
return visitor_wrapper.execute();
}
template <typename OneOf_, typename Visitor_>
void accept(OneOf_ && one_of, Visitor_ && visitor)
{
accept_returning<void>(one_of, visitor);
}
template <typename Result_, typename... Funcs_>
struct LambdaVisitor;
template <typename Result_>
struct LambdaVisitor<Result_>
{
void visit(struct NotReallyAType);
};
template <typename Result_, typename Func_, typename... Rest_>
struct LambdaVisitor<Result_, Func_, Rest_...> :
LambdaVisitor<Result_, Rest_...>
{
Func_ & func;
LambdaVisitor(Func_ & f, Rest_ & ... rest) :
LambdaVisitor<Result_, Rest_...>(rest...),
func(f)
{
}
Result_ visit(typename LambdaParameterTypes<Func_>::FirstParameterType & v)
{
return func(v);
}
using LambdaVisitor<Result_, Rest_...>::visit;
};
template <typename... Funcs_>
struct AllReturnSame;
template <typename Func_>
struct AllReturnSame<Func_>
{
enum { value = true };
};
template <typename A_, typename B_, typename... Funcs_>
struct AllReturnSame<A_, B_, Funcs_...>
{
enum { value = std::is_same<typename LambdaParameterTypes<A_>::ReturnType, typename LambdaParameterTypes<B_>::ReturnType>::value &&
AllReturnSame<B_, Funcs_...>::value };
};
template <typename...>
struct SeenSoFar
{
};
template <typename...>
struct ExtendSeenSoFar;
template <typename New_, typename... Current_>
struct ExtendSeenSoFar<New_, SeenSoFar<Current_...> >
{
typedef SeenSoFar<Current_..., New_> Type;
};
template <typename...>
struct AlreadySeen;
template <typename Query_>
struct AlreadySeen<SeenSoFar<>, Query_>
{
enum { value = false };
};
template <typename Query_, typename A_, typename... Rest_>
struct AlreadySeen<SeenSoFar<A_, Rest_...>, Query_>
{
enum { value = std::is_same<Query_, A_>::value || AlreadySeen<SeenSoFar<Rest_...>, Query_>::value };
};
template <typename...>
struct OneOfDeduplicatorBuilder;
template <typename... Values_>
struct OneOfDeduplicatorBuilder<SeenSoFar<Values_...> >
{
typedef OneOf<Values_...> Type;
};
template <typename SeenSoFar_, typename Next_, typename... Funcs_>
struct OneOfDeduplicatorBuilder<SeenSoFar_, Next_, Funcs_...>
{
typedef typename std::conditional<
AlreadySeen<SeenSoFar_, Next_>::value,
typename OneOfDeduplicatorBuilder<SeenSoFar_, Funcs_...>::Type,
typename OneOfDeduplicatorBuilder<typename ExtendSeenSoFar<Next_, SeenSoFar_>::Type, Funcs_...>::Type
>::type Type;
};
template <typename... Funcs_>
struct OneOfDeduplicator
{
typedef typename OneOfDeduplicatorBuilder<SeenSoFar<>, Funcs_...>::Type Type;
};
template <typename... Funcs_>
struct WhenReturnType;
template <typename FirstFunc_, typename... Funcs_>
struct WhenReturnType<FirstFunc_, Funcs_...>
{
typedef typename std::conditional<
AllReturnSame<FirstFunc_, Funcs_...>::value,
typename LambdaParameterTypes<FirstFunc_>::ReturnType,
typename OneOfDeduplicator<
typename LambdaParameterTypes<FirstFunc_>::ReturnType,
typename LambdaParameterTypes<Funcs_>::ReturnType ...>::Type
>::type Type;
};
template <typename Val_, typename... Funcs_>
typename WhenReturnType<Funcs_...>::Type
when(Val_ && val, Funcs_ && ... funcs)
{
LambdaVisitor<typename WhenReturnType<Funcs_...>::Type, Funcs_...> visitor(funcs...);
return accept_returning<typename WhenReturnType<Funcs_...>::Type>(val, visitor);
}
以下是特定于应用程序的代码:
struct Base {
Base(const string &i = "Base"):i(i){}
virtual ~Base(){}
string i;
};
struct A : public Base {
A():Base("A"){}
};
struct B : public Base {
B():Base("B"){}
};
struct C : public Base {
C():Base("C"){}
};
struct Node {
template<typename ContainerObjectType>
void castAndAdd(const shared_ptr<Base> &base, vector<ContainerObjectType> &container){
//base case do nothing
}
template<typename ContainerObjectType, typename T>
void castAndAdd(const shared_ptr<Base> &base, vector<ContainerObjectType> &container){
if(typeid(*base) == typeid(T)){
cout << "Adding: " << base->i << endl;
container.push_back(std::static_pointer_cast<T>(base));
}
}
template<typename ContainerObjectType, typename T, typename T2, typename ...V>
void castAndAdd(const shared_ptr<Base> &base, vector<ContainerObjectType> &container){
if(typeid(*base) == typeid(T)){
cout << "Adding: " << base->i << endl;
container.push_back(std::static_pointer_cast<T>(base));
}else{
castAndAdd<ContainerObjectType, T2, V...>(base, container);
}
}
template<typename ...T>
vector<OneOf<shared_ptr<T>...>> components(){
vector<OneOf<shared_ptr<T>...>> results;
for(auto&& item : contents){
castAndAdd<OneOf<shared_ptr<T>...>, T...>(item, results);
}
return results;
}
vector<shared_ptr<Base>> contents;
};
int main() {
Node root;
root.contents.push_back(make_shared<Base>());
root.contents.push_back(make_shared<C>());
root.contents.push_back(make_shared<B>());
root.contents.push_back(make_shared<A>());
root.contents.push_back(make_shared<C>());
auto components = root.components<A, B>();
for(auto&& component : components){
when(component,
[](const std::shared_ptr<B> &item){
cout << "B found: " << item->i << std::endl;
},
[](const std::shared_ptr<C> &item){
cout << "C found: " << item->i << std::endl;
},
[](const std::shared_ptr<A> &item){
cout << "A found: " << item->i << std::endl;
});
}
return 0;
}