Search code examples
c++templatesvisual-c++lambdageneric-programming

Is it possible to pass a lambda with a capture and parameters to another function? If so, how?


I'm trying to create a general input validation function (in C++17) that can take in a function pointer that returns a boolean to test if the input is valid or not. For example, if I want a user to enter a value less than 6, the body of the lambda would be return input < 6.

The problem arises with what I'm trying to do. I'm making a trivial console game to get more familiar with C++. I have a function called Change_Weapon that takes in a vector of all the weapons the user owns. From this, the following lambda comes about:

// Variables renamed for clarity
auto validation_func = [&owned_weapon_vec](unsigned input_to_test) -> bool
{
    return input_to_test > 0 && input_to_test <= owned_weapon_vec.size();
};

// Function call to input validation function
auto weapNum = valid_input<unsigned>(validation_func);

This is what my input validation function looks like.

template<typename Ty>
Ty valid_input(std::function<bool(Ty)>(valid_input_function))
{
    Ty input_val;
    bool first_loop = true;

    do
    { // Input value validiation loop
        if (first_loop) first_loop = false;
        else
        {
            std::cout << "Invalid entry value. Please re-enter: ";
            std::cin.clear();
            std::cin.ignore(/* Max possible value of long long */, '\n');
        }

        while (!(std::cin >> input_val))
        { // Input type validation loop
            std::cout << "Invalid entry type. Please re-enter: ";
            std::cin.clear();
            std::cin.ignore(/* Max val of long long*/, '\n');
        }
    } while (!valid_input_function(input_val))
}

Some things to note:

  • The function being passed will always take in the input_val as a parameter and that will be its only parameter
  • The function being passed will always return a boolean value
  • The function being passed should be able to be an anonymous lambda (or at least a named lambda)

I've tried various other posts, but couldn't seem to find an answer that talked about both a lambda capture and having parameters. Currently, Resharper is telling me:

No viable function
Argument types: '<lambda>'.
Candidates considered:
  unsigned valid_input<unsigned>(std::function<bool(unsigned)> valid_input_function)
    conversion of 1st argument 'validation_func' is ill-formed: cannot convert lvalue of 
    type '<lambda>' to parameter type 'std::function::<bool(unsigned)>'

Solution

  • How do you invoke your valid_input function? For me it works OK like this:

    template<typename T>
    T valid_input(std::function<bool(T)> predicate)
    {
      T some_input(69);  // I do not know where do you want to take it from
      std::cout << "predicate returns: " << predicate(some_input) << std::endl;
      return some_input;
    }
    
    int main()
    {
      size_t some_threshold(0);
      size_t result = valid_input<size_t>([some_threshold](size_t input) -> bool
      {
        std::cout << "predicate invoked with " << input << std::endl;
        return input > some_threshold;
      });
      std::cout << result << std::endl;
    }
    

    Or if you don't want to use template argument of valid_input explicitly you can explicitly assign lambda to std::function first:

    std::function<bool(size_t)> f = [some_threshold](size_t input)
    {
      std::cout << "predicate invoked with " << input << std::endl;
      return input > some_threshold;
    };
    size_t result = valid_input(f);