Własne biblioteki do konsoli zamiast obecnych,oraz unikanie std::cin >> oraz std::cout<< ? Czy New Curses ?

0

Chodzi głównie o następujące funkcje:

  • system("cls")
  • getline(std::cin,std::string variable)
  • getchar()
  • Sleep()

Słyszałam wielokrotnie,że stosowanie tych funkcji w aplikacjach konsolowych jest niepoprawne.Ponadto chce unikać używania std::cout<< oraz std::cin (nie wiem czy to dobra praktyka).

Czy dobrym pomysłem jest stworzenie sobie takiej mikrobiblioteki np. Console.h,która będzie zawierać funkcje będące odpowiednikami wyżej wymienionych ? Jeśli tak to jakie jeszcze funkcje,które mogę spotkać w poradnikach nie powinny być stosowane ?
Jeśli chodzi o taki system("cls") to nie potrafię znaleźć niczego innego niż czyszczenie konsoli przez windows.h a zależy mi na multiplatformości

Czy lepszym rozwiązaniem byłoby New Curses ?

0

Słyszałam wielokrotnie,że stosowanie tych funkcji w aplikacjach konsolowych jest niepoprawne.

Gdzie takie bzdury słyszałaś?

Sleep

To funkcja windowsowa. Jak chcesz multiplatformowo, to std::this_thread::sleep_for.

4

Tok myślenia popieram, choć o szczegółach będę się spierał.

  • Wkurza mnie system("cls") lub "pause" a i pewnie nie jest przenośny
  • getline ma sens, problemem jest mieszanie
  • Nie ma przekonania do śmiertelnego zamachu na getchar(). Zabiłbym wszystko z conio.h (w tym getch() )jako pomijające FILE/ stream (na architekturze 16bit to używało BIOS-u)
  • Slepp() widzę jako złe w czystych programach konsolowych

Nie wiem co masz na myśli co do* "chce unikać używania std::cout<< oraz std::cin "*. Zależy czego się domyślę:

  • jestem przeciwny hardcodowania cin w metodach,
  • ale za w pełni eleganckie uważam przekazanie streama w parametrze (przez referencję)
1

To zależy o jakich programach mówimy. Jak to wprawki lub proste "sprawdzę czy działa", nie ma sensu się spierać co do funkcji które podałeś.
Jeśli jednak pytasz o poważniejsze zastosowania, owszem... std::system(...) jest toksyczny/niebezpieczny https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152177
Uśpienia sleep(...), w większych programach są często krzykiem rozpaczy. Tu przysnę, tam przysnę może zadziała. We wprawkach, IMHO dopuszczalne. Oczywiście że lepiej użyć zegara lub innego mechanizmu budzenia (choćby CV dla wielowątkowości), niż blokować program/wątek na uśpieniu.
Strumienie w praktyce czasem są wolniejsze niż wywołania niższego poziomu lub z C. No ale to i tak dyskusja jak dorobić do hulajnogi spojlery :)

Ogólnie, bardziej wyrafinowane konsole, lepiej obsłużyć ncurses. Jest przecież (działająca) implementacja pod Win32.

2

Każde narzędzie am swój cel i przeznaczenie.
std::cin i std::cout to tylko odwzorowanie funkcjonalności standardowego wejścia i wyjścia.
Jest od groma aplikacji, którym to wystarczy i używanie ich ma sens

Jak chcesz pisać grę typu Snake, to wtedy std::cin i std::cout do niczego się nie nadaje (a wielu próbuje).

Biorąc pod uwagę, że jesteś początkujący lepiej najpierw zastanów się jaką aplikację chcesz napisać.
Potem zastanów się, czego najlepiej użyć: standardowego IO czy konsoli za pomocą ncurses.

3

Lepiej nie wynajdywać koła na nowo, tu raczej się to nie oplaci (chyba, ze chcesz dobrze zrozumieć systemowe API). Dla aplikacji imitujących GUI (np. mc, vim, itp.) warto użyć ncurses. To dobra, przenośna biblioteka, pewien jej podzbiór można przenieść nawet na bare-metal (coreboot/libpayload). To wyeliminuje też potrzebę System("cls"), które rzeczywiscie nie jest fajne. Dla pozostalych aplikacji standardowe strumienie są OK, zwlaszcza dla takich aplikacji jak curl, którego wyjscie najczęściej przekierujemy dalej.
Co się tyczy Sleepa, zależy do czego, zwykle jednak nie jest to najlepszy sposób.

5

Nauka ncurses jako osobnej biblioteki - ma sens (pytanie czy już na tym etapie? Imo trochę wcześnie)

Najczęstsze problemy z obsługą I/O u początkujących lub słabych programistów są architekturalne, a nie związane z użyciem konkretnej biblioteki. Jeśli funkcja sin(x) zacznie pytać użytkownika o podanie wartości to będzie to tak samo złe gdy użyje do tego cin/cout, ncursers albo gdy wyśle użytkownikowi maila.

Biblioteka standardowa, nawet jeśli jest trochę ułomna, to jest wciąż standardowa, co oznacza, że każdy programista języka będzie w miarę bezproblemowo zrozumieć co dany kod robi. Tworzenie własnej to zaciemni. Największy problem jest już wyżej wymieniony - mieszanie cin i getline, ale to wiąże się z brakiem zrozumienia jak one działają.

4

Uzyskałeś strasznie dobre odpowiedzi, dlatego ja skupię się na wytłumaczeniu dlaczego:

stosowanie tych funkcji w aplikacjach konsolowych jest niepoprawne

Stosowanie tych funkcji jest w większości poprawne, ale pod warunkiem że znasz ryzyko i ich zastosowanie.
system("cls")
Ta funkcja służy do wywoływania komend środowiska, jest ona strasznie przydatna gdy chcesz wykonać jakąś konkretną komendę, ale nie powinieneś jej stosować do czyszczenia ekranu ani do wymuszania oczekiwania na klawisz ("pause"), te funkcjonalności w łatwy sposób uzyskasz przy pomocy kilku linijek w C++.
Użycie konkretnych komend może wyeliminować tobie przenośność kodu ograniczając go do danego środowiska (np. Windows)
Opis funkcji: https://en.cppreference.com/w/cpp/utility/program/system

getline(std::cin,std::string variable)
Funkcja która wczytuje ciąg z strumienia aż do napotkania znaku nowej linij.
Głównym zagrożeniem w stosowaniu tej funkcji występuję podczas używania jej wraz z std::cin >>, może ona wtedy niezabezpieczona powodować że część danych nie będzie odczytana w oczekiwanej kolejności, jest to związane z tym jak poszczególne funkcje zarządzają buforem strumienia.
Opis funkcji: https://en.cppreference.com/w/cpp/string/basic_string/getline

getchar()
Funkcja do odczytywania znaku z strumienia wejścia.
Głównym zagrożeniem tutaj że kod który używa tą funkcje musi być dobrze zabezpieczony.
Opis funkcji: https://en.cppreference.com/w/cpp/io/c/getchar

Sleep()
Funkcja usypiająca aktualny wątek na X czasu.
Użycie tej funkcji eliminuje całkowicie przenośność kodu, zamiast tego w 100% powinieneś używać std::this_thread::sleep_for (https://en.cppreference.com/w/cpp/thread/sleep_for)
Opis funkcji: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep

Z swojej strony także polecam naukę ncurses :)

Pisane na szybko

0

Dziękuje za dobre wyjaśnienie tematu!

Nauka ncurses jako osobnej biblioteki - ma sens (pytanie czy już na tym etapie? Imo trochę wcześnie)

Najczęstsze problemy z obsługą I/O u początkujących lub słabych programistów są architekturalne, a nie związane z użyciem konkretnej biblioteki. Jeśli funkcja sin(x) zacznie pytać użytkownika o podanie wartości to będzie to tak samo złe gdy użyje do tego cin/cout, ncursers albo gdy wyśle użytkownikowi maila.

Biblioteka standardowa, nawet jeśli jest trochę ułomna, to jest wciąż standardowa, co oznacza, że każdy programista języka będzie w miarę bezproblemowo zrozumieć co dany kod robi. Tworzenie własnej to zaciemni. Największy problem jest już wyżej wymieniony - mieszanie cin i getline, ale to wiąże się z brakiem zrozumienia jak one działają.

Pozwolę wrzucić sobie przefabrykowaną funkcję getline (jako metoda klasy obiektowego kalkulatora,odpowiedzialna za wprowadzenie liczby)

void calculator::enterNumber(std::istream& input)
{
    char characters;
    std::string strNumber;
    strNumber.clear();
    while(input.get(characters) && characters != '\n')
    {
        strNumber.push_back(characters);
    }

    double number = atof(strNumber.c_str());
    numbers.push(number);
}

Oczywiście nie ma tutaj obsługi kontroli błędów wejścia (czyli wprowadzenia liter zamiat liczb),ale jak to widzisz zamiast używania std::cin >> ?
numbers to stos jak coś

0

Niestety słabo to widzę - zamiast przeczytać cin >> number; return number; muszę samodzielnie sparsować funkcję i zastnowić się co robi. Dodatkowo nie pomaga fakt, że funkcja ta nie zwraca wartości¹, zamiast tego pushując ją (na stos?), oraz używa liczby mnogiej dla pojedynczego znaku. Pomijam tu już problemy wydajnościowe z obsługą std::string, bo w wielu przypadkach będą zwyczajnie niezauważalne.

¹ to akurat może być sensowne w przypadku funkcji w klasie kalkulator

0
kq napisał(a):

¹ to akurat może być sensowne w przypadku funkcji w klasie kalkulator

klasa kalkulatora (przykładowa)

#ifndef CALCULATOR_H
#define CALCULATOR_H
#include <stack>
#include <iostream>

enum OPERATIONS{ADD=1,SUB=2,MULTI=3,DIVI=4,EXIT=5};

class calculator
{
    std::stack<double>numbers;
    OPERATIONS operation;
    bool errorFlag;
    public:
        calculator();
        ~calculator();
        double calculate();
        void enterNumber(std::istream& input);
        void enterOperation(std::istream&input);
    private:
        double addNumbers();
        double subNumbers();
        double multiNumbers();
        double diviNumbers();
        void exitCalculator();
};

#endif // CALCULATOR_H

Offtop-ując,jeśli chciałabym dodać funkcję wyświetlania historii operacji to w ramach myślenia "dziedziczymy coś co poszerza funkcjonalność bazy" (nie wiem czy to dobre myślenie) stworzyć klasę historyOperation, która dziedziczyłaby z tej klasy ?

0
Quanti994 napisał(a):

Offtop-ując,jeśli chciałabym dodać funkcję wyświetlania historii operacji to w ramach myślenia "dziedziczymy coś co poszerza funkcjonalność bazy" (nie wiem czy to dobre myślenie) stworzyć klasę historyOperation, która dziedziczyłaby z tej klasy ?

Zastanów się nad dziedziną którą modelujesz. Czy mając obiekt historii operacji, ma sens że jest on również kalkulatorem? Jeśli tak, to dziedziczenie ma sens. Jeśli nie, to absolutnie nie ma.

0

Projekt kalkulatora zaklada m.in. funkcje wyswietlenia pod menu ostatniej operacji jaka wykonal uzytkownik i aktualna liczbe będącą wynikiem operacji.Użytkownik będzie mógł do tej liczby dodać/odjąć/pomnożyć lub podzielić przez inną liczbę.

1 użytkowników online, w tym zalogowanych: 0, gości: 1