#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <typeinfo>
#include <typeindex>
#include <type_traits>
#include <utility>
#include <tuple>
using namespace std;
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;
}