c++callbackfunction-pointersvoid-pointers

Store void function pointer with arguments to be passed a called back


I'm working on a algorithm in C++ that sorts a list like linear structure without using any aid from any external data structure. My plan is to find the minimum value first and put it at the beginning to start comparing and ordering the rest of the values. I'm not working with STL since I'm trying to understand the foundations of the language. That learning method has represented a longer path in my journey but I've felt really well doing it.

My plan is to use iterate function as a main function that calls the following secondary functions through my linear structures: print to see the values in the console, getLast to be add nodes and findMin to find the minimum value to start with. I've read some Stack overflow answer about related issues that cover std::function, lambdas expressions and function pointers. I got confused at some point with that amount of info and then realized that that simplest yet most effective way to go is relying on function pointers. However, trying to store a reference to a function in a void(*callback)(Node* node) throws an error about not being able to find the address of my function.

void List::iterate(Node * node,void(*callBack)(Node* node)){
  callBack(node);
  if(node->next == nullptr)
    return;
  iterate(node->next,callBack); 
}

void List::add(int data){
  void (*callback)(Node* node) = &print(root); //Can't take the address of an rvalue of type void 
  iterate(root, callBack);
}

What I've found in the internet has been hard to apply to my code because of two reasons. One is that examples have a return value type and no parameters and the second one is that most examples aren't inside a class. Second reason resulted in problems with non-static values, especially when trying to use lambdas and std::function.

I'd like to ask the community if void functions have a special treatment. Also, my guess is that function with return values are easier to handle because a type represents a size of memory that can be allocated on compile time whereas void is clearly unknown. What's the theory behind this? In the meantime I'm gonna read this Stack Overflow post what-are-rvalues-lvalues-xvalues-glvalues-and-prvalues to understand better what the console is telling me.

Can anyone tell me what I'm doing wrong with my code? What concept am I missing/not applying well to the exercise? Also, if you feel you need to give extra feedback on the code please do, I'd highly appreciate that as well.

Here's the the exercise

/* Implement sorting in a dynamic linked list without using an additional array or data structure.*/

#include <iostream>

class List{
  struct Node{
    int data;
    Node* next = nullptr;
  };
  public:
    List(int data);
    ~List();
    void add(int data);
    void iterate(Node * node, void(*callback)(Node* node));
  private:
    void print(Node *node);
    void findMin(Node* node);
    void getLast(Node* node);
    int min = 0;
    Node* root = new Node;
    Node* it;
};

List::List(int data){
  root->data = data;
  it = root;
}

List::~List(){}

void List::print(Node *node){
  std::cout<<node->data<<'\n';
}

void List::findMin(Node* node){
  if (node->data<min) {
    min = node->data; 
  }
}

void List::getLast(Node* node){
  it = node; 
}

void List::iterate(Node * node,void(*callBack)(Node* node)){
  callBack(node);
  if(node->next == nullptr)
    return;
  iterate(node->next,callBack); 
}

void List::add(int data){
  void (*callback)(Node* node) = &print(root); //Can't take the address of an rvalue of type void 
  iterate(root, callBack);
}

int main(){
  List list(8); 
  list.add(9);
  list.add(7);
  list.add(4);
  list.add(3);

  return 0;
}

[Progress]

It's been great to read all comments from those stackoverflow members who to time to contribute to my question. I'm working on Igor Tandetnik comment at the moment, it's not just copying a pasting his snippets but reading why they work to understand when and how to use them for future reference. I'll post the solution very soon.

[Edit]

Initialized root with new Node in class declaration as suggested by PaulMcKenzie


Solution

  • Credits of this answer go to Igor Tandetnik. What definitely misled me to find out the answer was the non-static exception when trying to store a function as std::function without a lambda expression. Then I chose to find the answer with a function pointer that took me to nowhere.

    This is an example of the suggested lambda expression in List::add(int data) method.

    void List::add(int data){
      callback = [this](Node* node){getLast(node);}
      iterate(root,callback);
      it->next = new Node;
      it->next->data = data;
    }