0

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

wavesinaroom
  • 105
  • 8
  • 1
    *I'm not working with STL since I'm trying to understand the foundations of the language* -- The "foundations of the language" is to use what the language provides, and that is `std::list`. It's been part of C++ for over 25 years now. – PaulMcKenzie Apr 10 '23 at 02:36
  • `print(root);` is a function _call._ Presuming that you have a free function `void print((Node* node)` to get the address just use the fcn name, don't call it. So, `void (*callback)(Node* node) = print;` – Avi Berger Apr 10 '23 at 02:37
  • `print(root)` is not a function. It's the return value of a function call. And that function returns `void`; so you are trying to return an address of a result that doesn't exist. – Igor Tandetnik Apr 10 '23 at 02:37
  • @AviBerger Except that `print` is a non-static member function of `List`, and there's no way to convert that to a plain function pointer. – Igor Tandetnik Apr 10 '23 at 02:39
  • 2
    A plain function pointer like `void (*callback)(Node* node)` cannot represent an address of a non-static member function like `print`. You need `std::function` and lambdas for that; something like `std::function callback = [this](Node* node) { print(node); };` . Or alternatively, since everything is in the same class, a pointer-to-member like `void (List::*callback)(Node*) = &List::print;` which you then call as `(this->*callback)(node);` – Igor Tandetnik Apr 10 '23 at 02:42
  • @IgorTandetnik, indeed. I didn't see the member declaraion. Since it is not a free function/static member, it is incompatible with the type specified for the callback and cannot be used this way with the code as currently written. – Avi Berger Apr 10 '23 at 02:43
  • *I'm not working with STL* -- However nothing stops you from seeing *how* STL, namely algorithm functions like [std::sort(3)](https://en.cppreference.com/w/cpp/algorithm/sort) with a function pointer/object/lambda, accomplishes what you are trying to accomplish. What you are doing now is a `C`-coding style, where in C++, this has been superseded by using function objects, `std::function`, lambdas, etc. – PaulMcKenzie Apr 10 '23 at 02:45
  • " whereas void is clearly unknown." Actually it is 0. It takes no memory to store the return value of a function that doesn't have a return value. – Avi Berger Apr 10 '23 at 02:51
  • `List::List(int data){root->data = data;it = root;}` -- FYI -- This will end up attempting to use an uninitialized pointer. – PaulMcKenzie Apr 10 '23 at 02:53
  • *"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."* -- In other words, you're using [selection sort](https://en.wikipedia.org/wiki/Selection_sort)? – JaMiT Apr 10 '23 at 06:10
  • You have made **the worst choice using function pointers** as your starting point. You should've started with **`std::function`**. The next step would be to use templates for ease of use. Function pointers are not for C++ beginners; use of them needs deep understanding of C++. – Red.Wave Apr 10 '23 at 11:53
  • @PaulMcKenzie thanks for your comments! I understand that at some point working in the way I am is ignoring what's already there for you as a programmer. Wanting to invent the wheel is also silly but doing this sort of exercises always reminds me of a project I did with vanilla script using design patters. It was really tough, but later I started to learn React and everything was so simple that I was glad I'd spent time walking the long path before. Anyways, thanks taking time to comment on my question. – wavesinaroom Apr 11 '23 at 02:23
  • Yep @AviBerger! That's right, I finally got the difference between a function call and a function itself to be assigned to a variable. When I just leave `print` the compiler tells me that there's a problem with that assignment because it's inside a class. That's why looked for ways to use lambdas and `std::function`. Thanks for your comment! – wavesinaroom Apr 11 '23 at 02:27
  • @IgorTandetnik thanks a lot! I feel your comment is the closest answer to my question. I'm gonna try it in my code while reviewing `std::function` and lambdas as you suggest to understand the non-static exception I've got from the compiler. – wavesinaroom Apr 11 '23 at 02:30
  • @AviBerger by the way thanks for clarifying that a void pointer actually is 0 and takes no memory, that makes more sense to me. – wavesinaroom Apr 11 '23 at 02:32
  • That's correct @JaMiT! I did a little bit of research about selection sort during my planning stage before jumping into coding the solution. I'll use proper terminology next time to communicate better what I want to do. Thanks for pointing that out! – wavesinaroom Apr 11 '23 at 02:34
  • Cool @Red.Wave! That means that I've got the experience of going the wrong way and I now understand why my approach doesn't help me find the answer to my question. Also, while getting lost finding an answer I discovered if not explored things about C++ I only heard about but never dared to touch even it I was very likely to make mistakes because I'm a beginner. I'm glad all of that happened :) – wavesinaroom Apr 11 '23 at 02:38
  • @JuanJáuregui Perhaps I misunderstood or miscommunicated. I thought you were talking about the size of the return value (though I don't know why you would be doing that). A pointer to void or a pointer to a function returning void are different matters. Sorry. – Avi Berger Apr 11 '23 at 04:14
  • @JuanJáuregui some mistakes have long shadows. It is good to explore things, but not always. Too early envolvement with pointers specially function pointers may create a mental barier against further pursuit of C++. Function pointers in particular need lots of explaination; there are too many details about them to know, before you actually decide to use them. The very first purpose of declaration of `std::function` in C++11 was to simplify usage of storable functions. Once you start with function pointers,. You must already know the difference between them and the rest of callabales. – Red.Wave Apr 11 '23 at 09:43
  • @Red.Wave thanks for sharing your reasons! Next time I'll be more aware of what I want to achieve with an exercise. – wavesinaroom Apr 12 '23 at 02:42

1 Answers1

0

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;
}
wavesinaroom
  • 105
  • 8