• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Wiszące referencje w C++

Cloud VPS
0 głosów
137 wizyt
pytanie zadane 23 kwietnia w C i C++ przez whiteman808 Gaduła (4,820 p.)

Ma ktoś pomyśł jak naprawić problem wiszących referencji w metodzie `menu::add_submenu`? Jak w ogóle poprawić można mój kod?

main.cpp

// the purpose of the below code is to test menu class

#include "menu.h"
#include <cstdlib>
#include <iostream>

int main() {
    std::cin.exceptions(std::cin.eofbit);
    try {
        pt::menu main_menu{"Main menu"};
        main_menu.add_item("item 1",
                           []() { std::cout << "item 1" << std::endl; });
        main_menu.add_item("item 2",
                           []() { std::cout << "item 2" << std::endl; });
        {
            pt::menu submenu{"Submenu"};
            main_menu.add_submenu(submenu);
        }
        main_menu.loop();
    } catch (const std::exception& e) {
        std::cerr << e.what() << '\n';
        return EXIT_FAILURE;
    }
    std::cout << "Good bye!\n";
    return 0;
}

menu.h

#ifndef MENU_H_
#define MENU_H_

#include <functional>
#include <iosfwd>
#include <string>
#include <utility>
#include <vector>

namespace pt {

struct menu_item {
    menu_item(std::string title_val, std::function<void()> action_val);

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

    std::string title{};
    std::function<void()> action{};
};

class menu {
 public:
    explicit menu(std::string title_val, std::string description_val = "",
                  std::vector<menu_item> items_val = {});

    void add_item(menu_item item);
    void add_item(std::string title_val, std::function<void()> action);
    void add_item_at(int index, menu_item item);
    void add_item_at(int index, std::string title_val,
                     std::function<void()> action);
    void remove_item(int index);

    void add_submenu(menu menu_val);
    void add_submenu(std::string title_val, menu menu_val);
    void add_submenu_at(int index, menu menu_val);
    void add_submenu_at(int index, std::string title_val, menu menu_val);

    void set_title(std::string title_val);
    void set_description(std::string description_val);

    void loop() const;

 private:
    // helper functions
    void print_choices() const;
    char read_choice() const;
    void check_index(std::size_t index) const;
    void print_title(std::string_view title) const;

    // wrapper function for menu::loop
    std::function<void()> invoke_menu_loop(const menu& menu_val);

    static constexpr char quit_choice = 'q';
    static int nesting_level;
    std::string title{};
    std::string description{};
    std::vector<menu_item> items{};
};

}  // namespace pt

#endif  // MENU_H_

menu.cpp

#include "menu.h"
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>

namespace pt {

// menu_item constructors
menu_item::menu_item(std::string title_val, std::function<void()> action_val)
    : title{std::move(title_val)}, action{std::move(action_val)} {}

// menu_item struct i/o operators
std::ostream& operator<<(std::ostream& lhs, const menu_item& rhs) {
    return lhs << rhs.title;
}

// menu class constructors
menu::menu(std::string title_val, std::string description_val,
           std::vector<menu_item> items_val)
    : title{std::move(title_val)}, description{std::move(description_val)},
      items{std::move(items_val)} {}

// menu class static variables
int menu::nesting_level{0};

// menu class public interface
void menu::add_item(menu_item item) { items.push_back(std::move(item)); }

void menu::add_item(std::string title_val, std::function<void()> action_val) {
    items.emplace_back(std::move(title_val), std::move(action_val));
}

void menu::add_item_at(int index, menu_item item) {
    check_index(index);
    items.insert(items.begin() + index, std::move(item));
}

void menu::add_item_at(int index, std::string title_val,
                       std::function<void()> action) {
    check_index(index);
    items.emplace(items.begin() + index, std::move(title_val),
                  std::move(action));
}

void menu::remove_item(int index) {
    check_index(index);
    items.erase(items.begin() + index);
}

void menu::add_submenu(menu menu_val) {
    items.emplace_back(menu_val.title, invoke_menu_loop(menu_val));
}

void menu::add_submenu(std::string title_val, menu menu_val) {
    items.emplace_back(title_val, invoke_menu_loop(menu_val));
}

void menu::add_submenu_at(int index, menu menu_val) {
    check_index(index);
    items.insert(items.begin() + index,
                 menu_item{menu_val.title, invoke_menu_loop(menu_val)});
}

void menu::add_submenu_at(int index, std::string title_val, menu menu_val) {
    check_index(index);
    items.insert(items.begin() + index,
                 menu_item{title_val, invoke_menu_loop(menu_val)});
}

void menu::set_title(std::string title_val) { title = std::move(title_val); }

void menu::set_description(std::string description_val) {
    description = std::move(description_val);
}

void menu::loop() const {
    bool active{true};
    ++nesting_level;
    while (active) {
        print_title(title);
        if (!description.empty()) {
            std::cout << std::endl;
            std::cout << description;
            std::cout << std::endl;
        }
        char choice{read_choice()};
        if (choice == quit_choice) {
            --nesting_level;
            active = false;
        } else {
            std::cout << '\n';
            std::size_t index{static_cast<std::size_t>(choice) - '0'};
            if (index > items.size()) {
                std::cout << "Invalid choice!\n";
                continue;
            }
            // call function bound to menu item
            items[choice - '1'].action();
            // wait until user press the enter key
            std::cout << "\nPress enter to continue..." << std::endl;
            std::string line;
            std::getline(std::cin, line);
        }
    }
}

// menu class private functions
void menu::print_choices() const {
    for (std::size_t i = 0; i < items.size(); ++i) {
        std::cout << i + 1 << ") " << items[i] << '\n';
    }
    std::cout << "q) " << (nesting_level > 1 ? "Back" : "Quit");
    std::cout << std::endl;
}

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

    std::cout << "What do you want to do?\n";
    do {
        print_choices();
        std::cout << "Your choice: ";
        std::getline(std::cin, user_input);
        if (user_input.length() > 1 || user_input.empty()) {
            std::cout << "Please enter the valid choice.\n";
            continue;
        }
        choice = tolower(user_input.at(0));
        if ((choice - '1') < 0 ||
            (static_cast<std::size_t>(choice - '1') >= items.size() &&
             (choice != quit_choice))) {
            std::cout << "Invalid choice!\n";
        } else {
            valid = true;
        }
    } while (!valid);
    return choice;
}

void menu::check_index(std::size_t index) const {
    const std::size_t max_allowed_idx{items.size() - 1};
    if (index > max_allowed_idx) {
        std::ostringstream oss;
        oss << "index value must be between " << index << " and "
            << max_allowed_idx;
        throw std::out_of_range{oss.str()};
    }
}

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

// wrapper function for menu::loop
std::function<void()> menu::invoke_menu_loop(const menu& the_menu) {
    return [&the_menu]() { the_menu.loop(); };
}

}  // namespace pt

1 odpowiedź

0 głosów
odpowiedź 23 kwietnia przez mokrowski Mędrzec (158,800 p.)
"Sprytne wskaźniki" (nie lubię nazwy inteligentne choć po taką występują w literaturze) rozwiązują także i te problemy: https://en.cppreference.com/w/cpp/header/memory
komentarz 23 kwietnia przez whiteman808 Gaduła (4,820 p.)
Gdzie te smart pointery miałbym przechowywać?
komentarz 23 kwietnia przez mokrowski Mędrzec (158,800 p.)
W kontenerze. Radzę najpierw się z nimi zapoznać. Jakość Twoich pytań dzięki temu także wzrośnie.
komentarz 24 kwietnia przez whiteman808 Gaduła (4,820 p.)
Jakie ma zalety przechowywanie std::shared_ptr zamiast zwykłych obiektów menu? Czemu nie użyć zwykłego przekazania przez wartość w lambdzie?
komentarz 24 kwietnia przez mokrowski Mędrzec (158,800 p.)
W zrozumieniu zasad programowania, bardzo pomaga operowanie ścieżką kinestetyczną. W ogólności jest to "zrobić->zrozumieć->poczuć/internalizować". Po sprawdzeniu nawet prostego kodu z użyciem std::shared_ptr/std::unique_ptr/std::weak_ptr (ale napisanego samodzielnie i pod samodzielny prosty problem), zrozumiesz jak to działa. Tego nie da Ci ani pytanie na forum ani konsultacja z AI. Także opisywanie całości zagadnienia tu, będzie mało efektywne.

Podobne pytania

0 głosów
3 odpowiedzi 1,087 wizyt
pytanie zadane 18 lipca 2018 w C i C++ przez Nowacx02 Obywatel (1,060 p.)
+1 głos
6 odpowiedzi 4,215 wizyt
pytanie zadane 26 kwietnia 2015 w C i C++ przez SebekMakaron Obywatel (1,290 p.)
0 głosów
1 odpowiedź 724 wizyt
pytanie zadane 28 kwietnia 2020 w C i C++ przez Adrian Maj Nowicjusz (160 p.)

93,455 zapytań

142,449 odpowiedzi

322,719 komentarzy

62,837 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

Kursy INF.02 i INF.03
...