Question

I have a class like this:

template <typename... Types>
class Evaluator
{
public:
    template <typename... Types>
    Evaluator(Types... args)
    {
        list = std::make_tuple(args...);
    }

    template <typename T>
    bool Evaluate(const T& input)
    {
        // based on a specific input type T, here I want to call
        // Evaluate(input) for a specific element in the tuple. i.e. the
        // element that has method Evaluate, for which Evaluate(input) compiles 
        return std::get<0>(list).Evaluate(input);
    }

private:
    std::tuple<Types...> list;

};

Update The function could return false for instances that don't have proper "Evaluate(input) -> bool" function and is evaluated for all matching with bool result ||

Was it helpful?

Solution 2

First of all, we need a metafunction which can tell us whether the expression declval<T>().Evaluate(input) makes sense for a given type T.

We can use SFINAE and decltype in order to do so:

template<class ... Arguments>
struct CanEvaluate
{
    template<class T, class Enable = void>
    struct eval : std::false_type {};

    template<class T>
    struct eval<T,
        decltype( void( std::declval<T>().Evaluate(std::declval<Arguments>() ... ) ) ) > : std::true_type {};
};

Now we can write a single class MultiEvaluateFromTuple.

template<class TupleType, class ... InputTypes>
struct MultiEvaluateFromTuple
{
private:
    template<int I,int S,class Dummy = void>
    struct CheckEvaluate : CanEvaluate<InputTypes...>::template eval<typename std::tuple_element<I,TupleType>::type> {};

    //We need this because we can't instantiate std::tuple_element<S,TupleType>
    template<int S> struct CheckEvaluate<S,S> : std::false_type {};

    // Forward to the next element
    template<int I,int S, class Enabler = void>
    struct Impl {
        static bool eval(const TupleType & r, const InputTypes & ... input) {
            return Impl<I+1,S>::eval(r,input...);
        }
    };

    // Call T::Evalute()
    template<int I,int S>
    struct Impl<I,S, typename std::enable_if<CheckEvaluate<I,S>::value>::type> {

        static bool eval(const TupleType & r, const InputTypes & ... input) {
            bool Lhs = std::get<I>(r).Evaluate(input...);
            bool Rhs = Impl<I+1,S>::eval(r,input...);
            return Lhs || Rhs;
        }
    };

    //! Termination
    template<int S>
    struct Impl<S,S> {
        static bool eval(const TupleType & r, const InputTypes & ... input) {
            return false;
        }
    };

public:
    static bool eval(const TupleType & r,const InputTypes & ... input) {
        return Impl<0, std::tuple_size<TupleType>::value>::eval(r,input...);
    }
};

Usage:

return MultiEvaluateFromTuple<std::tuple<Types...>,T>::eval(list,input);

This will call Evaluate for all the types T in Types for which CanEvaluate<InputType>::eval<T>::value == true, and return the || of the results.

OTHER TIPS

Something like this:

// Unspecialized form, when the current element doesn't match. Tries the next one.
template <typename Tuple, int I, typename Argument, typename = void>
struct CallEvaluate : CallEvaluate<Tuple, I+1, Argument> {};

// Termination case, when the end of the tuple was reached. Has no operator () and will
// cause a compilation error.
template <typename Tuple, typename Argument>
struct CallEvaluate<Tuple, std::tuple_size<I>::value, Argument> {}; // no type fits

// Termination case, when the call std::get<I>(list).Evaluate(input) is valid.
template <typename Tuple, int I, typename Argument>
struct CallEvaluate<Tuple, I, Argument,
                    decltype(void(
                      std::declval<typename std::tuple_element<Tuple, I>::type>()
                        .Evaluate(std::declval<const Argument&>())))> {
   bool operator ()(const Tuple& list, const Argument& input) const {
     return std::get<I>(list).Evaluate(input);
   }
};


// Use:
CallEvaluate<decltype(list), 0, T>()(list, input);

I'm not sure exactly what you're trying to do. Here's what I think is the solution:

template <typename... Types>
class Evaluator
{
private:
    std::tuple<Types...> list;

    template <typename T>
    struct has_evaluator
    {
        typedef char yes;
        typedef char no[2];

        template <typename C, C>
        struct S;

        template <typename U>
        yes& check(S<bool T::*, &T::Evaluate>*);

        template <typename U>
        no& check(...);

        static const bool value = sizeof(check<T>(nullptr)) == sizeof(char);
    };
public:
    template <typename... Args>
    Evaluator(Args&&... args) : list(std::make_tuple(std::forward<Args>(args)...))
    { }

    template <typename T,
              typename = typename std::enable_if<
                  has_evaluator<typename std::tuple_element<0, decltype(list)>::type>::value>::type>
    auto Evaluate(const T& input) -> decltype(std::get<0>(list).Evaluate(input), bool())
    {
        return std::get<0>(list).Evaluate(input);
    }
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top