简体   繁体   中英

Declaring a function that can take a lambda as a parameter, but that lambda must be able to capture other variables

I would like to make a function that can take a function as a parameter, so that I can call the function with a specified lambda in the client code. Currently my code is this:

void Execute(float threshold,
               void (*behaviour)(std::shared_ptr<cv::Mat>& image_i,
                       pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud_i,
                       const int* position, int index)) 

However this will not compile. using :

  template <typename func>
  void Execute(float threshold, func behaviour)

Will compile and work fine but I would like to constrain the client code to adhere to the function signature at compile time. I am using C++17.

Example client code:

  caster.Execute(thresh,
                 [&](std::shared_ptr<cv::Mat>& image,
                     pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud,
                     const int* position, int index) -> void {
                   pcl::PointXYZ origin(0, 0, 0);
                   float d = beam::distance(cloud->points[index], origin);
                   if (d > max_depth_) { max_depth_ = d; }
                   if (d < min_depth_) { min_depth_  = d; }
                   image->at<float>(position[0], position[1]) = d;
                   num_extracted++;
                 });

As you can see I would like to access variables num_extracted, min_depth_ and max_depth_ which are declared outside the scope of the lambda.

You are asking to meet two requirements:

  1. Constrain any lambda to a given signature.
  2. Access specific variables from within a given lambda.

Here's a working version of what I could infer from your code:

namespace cv {
    struct Mat {
        float  m_mat [3][3] {};
        template<typename TYPE_T>
        float&  at( int ROW, int COL ) { return m_mat[ROW][COL]; }
    };
}

namespace pcl {
    struct  PointXYZ { int X{}, Y{}, Z{}; };

    template<typename TYPE_T>
    struct  PointCloud {
        using Ptr = std::shared_ptr<PointCloud<TYPE_T>>;
        TYPE_T *points{};
        int     length{};
    };
}

struct beam {
    static float  distance( const pcl::PointXYZ &PT, const pcl::PointXYZ &ORIGIN ) { return 0.0f; }
};

std::shared_ptr<cv::Mat>             g_image       { new cv::Mat{} };
pcl::PointCloud<pcl::PointXYZ>::Ptr  g_cloud       { new pcl::PointCloud<pcl::PointXYZ>{} };
int                                  g_position [] { 0, 0 };

template<typename func>
requires std::is_invocable_v<func,
    std::shared_ptr<cv::Mat>&, 
    pcl::PointCloud<pcl::PointXYZ>::Ptr&,
    const int*,
    int
>
void  Execute( float threshold, func behaviour )
{
    behaviour(g_image, g_cloud, g_position, 1);
}

int main()
{
    int    num_extracted {};
    float  min_depth_    {},
           max_depth_    {};

    Execute(1.0f, [&] ( auto &&IMAGE, auto &&CLOUD, auto &&POSITION, auto &&INDEX )
    {
        pcl::PointXYZ origin { 0, 0, 0 };
        float d = beam::distance(CLOUD->points[INDEX], origin);
        if( d > max_depth_ ) { max_depth_ = d; }
        if( d < min_depth_ ) { min_depth_ = d; }
        IMAGE->at<float>(POSITION[0], POSITION[1]) = d;
        num_extracted++;
    });

    return 0;
}
  1. There are a couple ways to constrain the lambda signature, but they all involve using std::is_invovable (or making behaviour a std::function, as others have suggested). I've opted to use the newer requires syntax. See: C++ Constraints and Concepts

  2. There are two sets of variables you need, but you haven't described where they will come from: i) Those to be passed to Execute, and ii) Those to be used in a specific lambda that will be passed to Execute.

2.i) These are the g_* variables. They must be visible from the scope Execute is being called in so they can be passed to the invocable behavior. In this case Execute is a global function so the variables must also be global.

2.ii) These are the variables in main. They must be visible from the scope the lambda is being created in. In this case they must be in main or global.

[EDIT] For C++ 17 you can change Execute from:

template<typename func> requires std::is_invocable_v<...>

to

template<typename func, typename = std::include_if_t<std::is_invocable_v<...>>>

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