Wskaźniki na funkcje - błąd kompilacji

Wskaźniki na funkcje - błąd kompilacji
whiteman808
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 201
0

Hej

Chcę przekazać jako parametr do konstruktora klasy menu_item wskaźnik na funkcję nie przyjmującą argumentów, która nie będzie nic zwracać. Funkcja ma być wywoływana w metodzie loop klasy menu. W wektorze w klasie menu mają być przechowywane obiekty klasy menu_item.

Co mam zrobić aby móc wywoływać funkcje wskazywane przez zmienne typu menu_item_func zarówno wewnątrz klasy jak i przechowywać wskaźniki typu menu_item_func do funkcji poza klasą?

Podczas kompilacji main.cpp otrzymuję błąd

Kopiuj
g++ -o main main.cpp menu.cpp                                                                                            10:28:51
menu.cpp: In constructor ‘menu::menu(const std::string&)’:
menu.cpp:14:43: error: no matching function for call to ‘menu_item::menu_item(<brace-enclosed initializer list>)’
   14 |     items.push_back(menu_item{"Quit", quit});
      |                                           ^
In file included from menu.cpp:1:
menu.h:14:5: note: candidate: ‘menu_item::menu_item(const menu_item&)’
   14 |     menu_item(const menu_item& item) : title{item.title}, action{item.action} {}
      |     ^~~~~~~~~
menu.h:14:5: note:   candidate expects 1 argument, 2 provided
menu.h:12:5: note: candidate: ‘menu_item::menu_item(const std::string&, menu_item_func)’
   12 |     menu_item(const std::string& title_val, menu_item_func action_val)
      |     ^~~~~~~~~
menu.h:12:60: note:   no known conversion for argument 2 from ‘<unresolved overloaded function type>’ to ‘menu_item_func’ {aka ‘void (*)()’}
   12 |     menu_item(const std::string& title_val, menu_item_func action_val)
      |                                             ~~~~~~~~~~~~~~~^~~~~~~~~~
menu.h:11:5: note: candidate: ‘menu_item::menu_item(const std::string&)’
   11 |     menu_item(const std::string& title_val) : title{title_val} {}
      |     ^~~~~~~~~
menu.h:11:5: note:   candidate expects 1 argument, 2 provided
menu.cpp: In constructor ‘menu::menu(const std::string&, const std::vector<menu_item>&)’:
menu.cpp:21:43: error: no matching function for call to ‘menu_item::menu_item(<brace-enclosed initializer list>)’
   21 |     items.push_back(menu_item{"Quit", quit});
      |                                           ^
menu.h:14:5: note: candidate: ‘menu_item::menu_item(const menu_item&)’
   14 |     menu_item(const menu_item& item) : title{item.title}, action{item.action} {}
      |     ^~~~~~~~~
menu.h:14:5: note:   candidate expects 1 argument, 2 provided
menu.h:12:5: note: candidate: ‘menu_item::menu_item(const std::string&, menu_item_func)’
   12 |     menu_item(const std::string& title_val, menu_item_func action_val)
      |     ^~~~~~~~~
menu.h:12:60: note:   no known conversion for argument 2 from ‘<unresolved overloaded function type>’ to ‘menu_item_func’ {aka ‘void (*)()’}
   12 |     menu_item(const std::string& title_val, menu_item_func action_val)
      |                                             ~~~~~~~~~~~~~~~^~~~~~~~~~
menu.h:11:5: note: candidate: ‘menu_item::menu_item(const std::string&)’
   11 |     menu_item(const std::string& title_val) : title{title_val} {}
      |     ^~~~~~~~~
menu.h:11:5: note:   candidate expects 1 argument, 2 provided
menu.cpp: In member function ‘bool menu::loop()’:
menu.cpp:35:1: warning: no return statement in function returning non-void [-Wreturn-type]
   35 | }
      | ^

Kod C++

Kopiuj
~/programy_cpp/linked_list ❯ cat menu.h                                                                                                               10:29:58
#ifndef MENU_H
#define MENU_H

#include <iostream>
#include <string>
#include <vector>

typedef void (*menu_item_func)();

struct menu_item {
    menu_item(const std::string& title_val) : title{title_val} {}
    menu_item(const std::string& title_val, menu_item_func action_val)
        : title{title_val}, action{action_val} {}
    menu_item(const menu_item& item) : title{item.title}, action{item.action} {}
    menu_item& operator=(const menu_item& rhs);

    friend std::ostream& operator<<(std::ostream& lhs, const menu_item& rhs);

    std::string title{};
    menu_item_func action{};
};

class menu {
public:
    menu(const std::string& title_val);
    menu(const std::string& title_val, const std::vector<menu_item>& items_val);
    menu(const menu& menu_val);

    bool loop();

private:
    void quit();

    bool active{false};
    std::string title{};
    std::vector<menu_item> items{};
};

#endif // MENU_H
~/programy_cpp/linked_list ❯ cat menu.cpp                                                                                                             10:30:08
#include "menu.h"

menu_item& menu_item::operator=(const menu_item& rhs) {
    title = rhs.title;
    action = rhs.action;
    return *this;
}

std::ostream& operator<<(std::ostream& lhs, const menu_item& rhs) {
    return lhs << rhs.title;
}

menu::menu(const std::string& title_val) : title{title_val} {
    items.push_back(menu_item{"Quit", quit});
}

menu::menu(const std::string& title_val, const std::vector<menu_item>& items_val) : title{title_val} {
    for (const menu_item& item : items) {
        items.push_back(item);
    }
    items.push_back(menu_item{"Quit", quit});
}

menu::menu(const menu& menu_val) {
    for (const menu_item& item : menu_val.items) {
        items.push_back(item);
    }
    items.push_back(menu_item{"Quit", });
}

bool menu::loop() {
    while (active) {

    }
}

void menu::quit() {
    active = false;
}
~/programy_cpp/linked_list ❯ cat main.cpp                                                                                                             10:30:12
#include <iostream>
/*#include <string>*/
/*#include "linked_list.h"*/
#include "menu.h"

void display_sth() {
    std::cout << "test\n";
}

int main() {
    menu_item item{"test", display_sth};
    menu test{"abc", {item}};
    return 0;
}

TomaszLiMoon
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 530
1

Typ wskaźnika dla metody klasy powinien być następujący

Kopiuj
typedef void (menu::*menu_item_func)();

i jego użycie

Kopiuj
items.push_back(menu_item{"Quit", &menu::quit});

Wektor items nie może zawierać równocześnie wskaźników do metod klas i zwykłych funkcji ( gdzie typ wskaźnika jest inny ) - musisz wybrać co chcesz tam trzymać.
IMO lepiej tutaj iść w std::function lub użyć wyrażeń typu lambda

whiteman808
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 201
0

Jakiś pomysł skąd błędy w trakcie wykonania programu? Wyjście z terminala poniżej:

Kopiuj
~/programy_cpp/linked_list ❯ cat menu.h                                                                                                         ✘ IOT 13:06:46
#ifndef MENU_H
#define MENU_H

#include <iostream>
#include <string>
#include <vector>
#include <functional>

using menu_item_func = std::function<void()>;

void print_title(const std::string& title);

struct menu_item {
    menu_item(const std::string& title_val) : title{title_val} {}
    menu_item(const std::string& title_val, menu_item_func action_val)
        : title{title_val}, action{action_val} {}
    menu_item(const menu_item& item) : title{item.title}, action{item.action} {}
    menu_item& operator=(const menu_item& rhs);

    friend std::ostream& operator<<(std::ostream& lhs, const menu_item& rhs);

    std::string title{};
    menu_item_func action{};
};

using menu_items = std::vector<menu_item>;

class menu {
public:
    menu(const std::string& title_val);
    menu(const std::string& title_val, const std::vector<menu_item>& items_val);
    menu(const menu& menu_val);

    void loop();

    static void noop() { std::cout << "No action defined!\n"; }

private:
    void print_choices() const;
    char read_choice() const;

    static int nesting_level;
    bool active{false};
    std::string title{};
    std::vector<menu_item> items{};
};

#endif // MENU_H
~/programy_cpp/linked_list ❯ cat menu.cpp                                                                                                             13:07:39
#include "menu.h"

int menu::nesting_level = 0;

void print_title(const std::string& title) {
    std::size_t title_len = title.size();
    std::size_t width = title_len + 4;
    for (int i = 0; i < width; ++i) {
        std::cout << "*";
    }
    std::cout << "\n* " << title << " *\n";
    for (int i = 0; i < width; ++i) {
        std::cout << "*";
    }
    std::cout << std::endl;
}

menu_item& menu_item::operator=(const menu_item& rhs) {
    title = rhs.title;
    action = rhs.action;
    return *this;
}

std::ostream& operator<<(std::ostream& lhs, const menu_item& rhs) {
    return lhs << rhs.title;
}

menu::menu(const std::string& title_val) : title{title_val} {
}

menu::menu(const std::string& title_val, const std::vector<menu_item>& items_val)
    : title{title_val}, items{items_val} {
}

menu::menu(const menu& menu_val) {
    for (const menu_item& item : menu_val.items) {
        items.push_back(item);
    }
}

void menu::print_choices() const {
    for (int i = 0; i < items.size(); ++i) {
        std::cout << i << ") " << items[i] << std::endl;
    }
    std::cout << "q) ";
    if (nesting_level > 1)
        std::cout << "Back";
    else
        std::cout << "Quit";
    std::cout << std::endl;
}

char menu::read_choice() const {
    char choice;
    bool valid{false};

    std::cout << "What do you want to do?\n";
    do {
        print_choices();
        std::cout << "Your choice: ";
        std::cin >> choice;
        if ((choice - '0') < 0 || ((choice - '0') >= items.size()) && (choice != 'q')) {
            std::cout << "Invalid choice!\n";
        } else {
            valid = true;
        }
    } while (!valid);
    return choice;
}

void menu::loop() {
    active = true;
    ++nesting_level;
    while (active) {
        print_title(title);
        char choice = read_choice();
        if (choice == 'q') {
            --nesting_level;
            active = false;
        } else {
            items[choice].action();
        }
    }
}
~/programy_cpp/linked_list ❯ cat main.cpp                                                                                                             13:07:41
#include <iostream>
/*#include <string>*/
/*#include "linked_list.h"*/
#include "menu.h"

void display_sth() {
    std::cout << "test\n";
}

int main() {
    menu_items main_menu_items;
    main_menu_items.emplace_back("test", menu::noop);
    main_menu_items.emplace_back("display_sth", display_sth);
    menu main_menu{"Main menu", main_menu_items};
    main_menu.loop();
    return 0;
}
~/programy_cpp/linked_list ❯ ./main                                                                                                                   13:07:47
*************
* Main menu *
*************
What do you want to do?
0) test
1) display_sth
q) Quit
Your choice: 1
/usr/lib/gcc/x86_64-pc-linux-gnu/14/include/g++-v14/bits/stl_vector.h:1130: std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::operator[](size_type) [with _Tp = menu_item; _Alloc = std::allocator<menu_item>; reference = menu_item&; size_type = long unsigned int]: Assertion '__n < this->size()' failed.
zsh: IOT instruction (core dumped)  ./main
~/programy_cpp/linked_list ❯ ./main                                                                                                             ✘ IOT 13:07:51
*************
* Main menu *
*************
What do you want to do?
0) test
1) display_sth
q) Quit
Your choice: 0
/usr/lib/gcc/x86_64-pc-linux-gnu/14/include/g++-v14/bits/stl_vector.h:1130: std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::operator[](size_type) [with _Tp = menu_item; _Alloc = std::allocator<menu_item>; reference = menu_item&; size_type = long unsigned int]: Assertion '__n < this->size()' failed.
zsh: IOT instruction (core dumped)  ./main
~/programy_cpp/linked_list ❯ ./main                                                                                                             ✘ IOT 13:07:55
*************
* Main menu *
*************
What do you want to do?
0) test
1) display_sth
q) Quit
Your choice: q

whiteman808
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 201
0

Wiem gdzie był błąd - zły indeks tablicy w metodzie menu::loop

Powinno być

Kopiuj
void menu::loop() {
    active = true;
    ++nesting_level;
    while (active) {
        print_title(title);
        char choice = read_choice();
        if (choice == 'q') {
            --nesting_level;
            active = false;
        } else {
            items[choice - '0'].action();
        }
    }
}

K1
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 55
0

@whiteman808
1 drobna uwaga na temat konstruktorów. Sam piszę aktualnie podobnie działającą bibliotekę (tylko, że z dziedziczeniem interfejsu i wskaźnikami) i podawanie wskaźnika na funkcje jako parametr nie zawsze jest najlepszym pomysłem. U mnie (na MSVC) kompilator miał problem z takimi funkcjami/metodami i wyrzucał niezrozumiałe błędy (najprawdopodobniej nieprawidłowo czytał alias). U mnie lepszym wyborem było danie szablonu w konstruktorze lub innej metodzie, ale jeśli u ciebie działa dobrze to być może po prostu miałem coś pomieszane w projekcie i niepotrzebnie ci zawracam głowę.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.