简体   繁体   中英

Call a variadic template functor in a variadic template parameter pack if it satisfies a condition

I have a variadic template functor class:

template <typename Result, typename... Arguments>
class Functor
{
public:
    using FunctionType = std::function<Result(Arguments...)>;
    Result operator() (const Arguments&... arguments) { return Function(arguments); }
    std::string GetName() { return Name; }
    Functor(const std::string& name, const FunctionType& function) : Name(name), Function(function) { }
private:
    std::string Name;
    FunctionType Function;
}

and a variadic template function:

template <typename... Functors>
void Function(const Functors&... functors) { }

Let's say I have declared some Functor s and want to use them in Function :

Functor<int, int> f
(
    "f",
    [](int x) -> int { return 2 * x - 1; }
);
Functor<int, int, int> g
(
    "g",
    [](int x, int y) -> int { return x + 2 * y - 3; }
);
Function(f, g);

Inside Function , I want to find out which Functor passed to it in the parameter pack satisfies a certain condition, and if it does, call the functor. Like the following pseudocode:

template <typename... Functors>
void Function(const Functors&... functors)
{
    foreach (Functor functor in functors)
    {
        if (functor.GetName() == "f")
        {
            functor(); // the functor can have different parameter lists so this is another problem
        }
    }
}

I want to know if there's a way to do this. I also want to know that since the Functor s can have varying parameter lists, how do I call them even if I were able to find the correct Functor to call? Suppose that there's a std::vector<int> and when the Functor takes three int s as its argument, is it possible to take the first three int s and pass them to the Functor ?

In C++17, this is easily solvable using fold expressions .

#include <functional>
#include <string>

template <typename Result, typename... Arguments>
class Functor
{
public:
    using FunctionType = std::function<Result(Arguments...)>;
    Result operator() (const Arguments&... args) {
        return this->Function(arguments...);
    }
    std::string GetName() {
        return this->Name;
    }
    Functor(const std::string& name, const FunctionType& function)
        : Name(name), Function(function) { }

private:
    std::string Name;
    FunctionType Function;
};

template <typename... Functors>
void ForEachFunctor(const Functors&... functors)
{
    ((functors.getName() == "f" && functors()), ...);
}

Here, we exploit the short-circuiting of the && operator. Only if the condition functors.getName() == "f" is true, will the right hand side of the operator be evaluated.

A slightly less hacky approach uses a separate function:

template <typename Functor>
void InvokeIfNamedF(const Functor &functor) {
    if (functor.GetName() == "f")
        functor();
}

template <typename... Functors>
void ForEachFunctor(const Functors&... functors)
{
    (InvokeIfNamedF(functors), ...);
}

The elements of the parameter pack are combined using the comma-operator. However, in this example, we are invoking each functor with no parameters. If all functors have distinct signatures then passing them as one pack and invoking them conditionally all at once is probably not possible.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM