1

I want to employ std::function to help me run a generic traverse function that traverses a BST and calls the parametrized function.

My difficulty is that the parametrized function varies in its own parameters.

So, for example, I was to generalize the following three functions (all different in their parameters).

//populates an array with the values in the BST by traversing the BST
void LinkedBST<T>::populate(T const * const data, size_t & I, Node *x)

{
      data[i++] = x->val;
}

//insert unique values of another BST into "this" BST: traverses the other BST and inserts every value
void LinkedBST<T>::insert(Node *x)
{
      insert(x->val);
}

Instead of writing a separate traverse function for each one of the aforementioned functions, I want to be able to pass them into one generic traverse function, something like:

void LinkedBST<T>::traverse(Node *x, auto func)
{
     if(x == nullptr)
          return;

     traverse(x->left, func);
     func( <parameters> );
     traverse(x->right, func);
}

Is there any way to do this? If there is, can you help me do it?

Thank you :)

2 Answers2

2

In general, you will need to find a way to standardise the signature to all of your traversal callbacks. One option is to use lambdas and make use of lambda captures to reduce the number of arguments to your functions.

void LinkedBST<T>::populate(T const * const data, size_t & I)
{
    traverse(root, [&](Node * x) {
            data[i++] = x->val;
        });
}

Note that the same traversal function couldn't be used for compare because you need to simultaneously traverse two trees. And it's not clear what insert is even supposed to be doing, but from the comment it sounds like it would require a simultaneous traversal as well.

Peter Bell
  • 371
  • 2
  • 6
1

One solution is to templatize your traverse function to take a function object. Then instead of specifying the parameters in the traverse function, move those parameters to the function object and let the function object's operator() handle the details when called:

template <typename func>
void LinkedBST<T>::traverse(Node *x, func fn)
{
     if(x == nullptr)
          return;

     traverse(x->left, fn);
     fn(x->val);
     traverse(x->right, fn);
}

struct some_func
{
   int param1;
   int param2;
   int param3;

   some_func(int p1, int p2, int p3) : param1(p1), param2(p2), param3(p3) {}
   void operator()(int node_value) 
   {
      std::cout << "The node value is " << node_value << "\n";
      // the parameters are param1, param2, param3
   }
};

When the operator() is invoked (the function is called), you now have the node value, plus all the parameters you set inside the object.

Then something like this can be done:

Node *node_ptr;
//...
LinkedBST<int> the_list;
//...
some_func f(1,2,3);  // we want to use 1,2,3 as the parameters to the custom function
the_list.traverse(node_ptr, f);

Here is a simplified version showing the basics by using a dummy class.


You could also use a lambda with this technique:

Node *node_ptr;
//...
LinkedBST<int> the_list;
//...
int arg1=1, arg2=2, arg3=3;
the_list.traverse(node_ptr, 
                  [&](int node_value){std::cout << "The node value is " << 
                                      node_value << "\n" << arg1 << " " << 
                                      arg2 << " " << arg3;});
PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45