Jeśli to jest funkcja jakiegoś interfejsu to można ją traktować jak "plik nagłówkowy" więc nie widzę w tym nic złego.
Ta funkcja, której fragment wkleiłem to main
. Jeśli szedłbym dalej w zwracanie kodu błędów to przewiduję, że takich if'ów mogłoby być całkiem sporo występujących po sobie.
Czy Sniffer to twój kod i możesz go modyfikować? (...) Kod który wrzuciłeś to typowy sposób pisania znany w języku C. Najbardziej idiomatycznym sposobem obecnie używanym w C++ są wyjątki.
Sniffer to mój kod - wkleiłem link do githuba w poście na dole. Co do wyjątków to wiem, że istnieją i użyłem ich do obsługi błędów w konstruktorze obiektu sniffer
, ale tylko dlatego, że przeczytałem na SO, iż wyjątki można spokojnie stosować do obsługi błędów w konstruktorach, bo one nie zwracają wartości. Niedawno napisałem taki temat odnośnie stosowania if'ów i wyjątków -> https://4programmers.net/Forum/C_i_C++/343224-wyjatki_a_ify_podczas_obslugi_bledow?p=1701217#id1701217 Z niego nauczyłem się, że wszystko co mogę obsłużyć i na co mam wpływ podczas implementacji algorytmu obsługuję bez użycia wyjątków, a wyjątki używam do obsługi błędów, na które wpływu nie mam. Na przykład jakiś bug w funkcji systemowej gdzieś wewnątrz spowodowałby, że OS nie zwróciłby mi uchwytu do pliku, z którego chciałem skorzystać, a plik w systemie istnieje. W takim przypadku użyłbym wyjątku, a tutaj użyłem if'ów zgodnie z nauką, którą z tamtego tematu wyniosłem. Nie wiem czy dobrze robię czy nie w końcu - wciąż się uczę. :D
(...) Jeśli błąd ma wędrować więcej niż 2 poziomy wyżej od miejsca powstania to sugeruję użyć wyjątki. Przekazywanie błędów w "głębokim kodzie" jako wyniku funkcji to droga do piekła ... Owszem czasem jest to szybsze i bardziej precyzyjne ale w sumie po to też są wyjątki, które przy okazji ogarną nam jeszcze inne nieprzewidziane sytuacje.
Czyli jeśli dojdę z wywołaniami funkcji gdzieś głębiej mogę użyć wyjątku do obsługi błędu zamiast schodzić w dół stosu, żeby było wygodniej? Dobrze rozumiem?
(...) Ja kieruję się prostą zasadą - jeżeli masz obiekt, to obiekt ma być używalny i już. Nie ma być tak, że potem jeszcze trzeba wywoływać pomocnicze metody, żeby doinicializować obiekt. Konstruktor i koniec.
Też o tym myślałem, ale mnie zawsze kłują w oczy długie funkcje. Wiem, że może to przesada i nie powinienem się tym nadto przejmować, ale jakoś mam alergię na takie działanie. Myślałem też o tym, że obiekt "skonstruować" tak, żeby był w pełni używalny, ale bałem się o rozmiar konstruktora. Czyli uważasz, że rozpiętość kodu konstruktora jest mniej ważna, a bardziej ważna jest pełna używalność obiektu po jego skonstruowaniu? Jeśli mogę taką zasadę stosować nie patrząc zbytnio na rozpiętość tego kodu to świetnie. :) Tzn. oczywiście muszę dopowiedzieć, że ten kod wewnątrz konstruktora i tak dzieliłbym na metody, żeby było możliwie krótko.
Nigdy nie byłem zwolennikiem rzucania wyjątków w konstruktorze. Szczególnie jeżeli mam do czynienia z nie-RAII typami, jak te z WinAPI, których używasz. W takich wypadkach wolałem zrobić metodę
wytwórczą czy jakąś fabrykę, która zajmuje się inicializacją i zwróci mi albo obiekt, albo info, że się nie powiodło (...)
O wyjątkach rzucanych w konstruktorach przeczytałem na SO. Szukałem info na temat tego jak obsłużyć błąd w konstruktorze, bo konstruktor nie zwraca żadnej wartości. Właśnie z tego powodu radzili, żeby użyć wyjątki.
W takich wypadkach wolałem zrobić metodę wytwórczą czy jakąś fabrykę, która zajmuje się inicializacją i zwróci mi albo obiekt, albo info, że się nie powiodło (np. std::option<Sniffer>).
O std::option
nie wiedziałem, dzięki za podrzucenie tematu. :) Ten projekt służy mi przede wszystkim do nauki C++ a finalnie być może przyda mi się do inżynierii wstecznej. Wiadomo, że jest Wireshark ze snifferów, ale jest bardzo rozbudowany - jak dla mnie aż za bardzo, a poza tym miło jest korzystać z projektów autorskich. :D
Podsumowując, w ja osobiście w Twoim przypadku przeniósłbym wszystko do metody wytwórczej, która zwraca std::option. Wywoałałaby ona wszystkie inicializacje z WinAPI, a potem przekazała do ctora Sniffera ogarnięte już wartości wsaData, snifferSock i netInterface.
I to jest bardzo ciekawe podejście, na które bym nie wpadł, bo nie wiedziałem nawet o istnieniu std::option
. Postaram się coś podobnego zaimplementować, bo brzmi dobrze. :)
Dziękuję wszystkim za pomoc. Jeszcze @katakrowa jakbyś mógł mi wyjaśnić czy dobrze zrozumiałem to wędrowanie 2 poziomy wyżej od miejsca powstania błędu
to byłbym wdzięczny.