Przekazywanie stringów - wtf?

0

Mam sobie klasę Filter (nieważne co to robi), pobiera dane z pliku, między innymi informacje o typie. Ustaliłem sobie, że dodatni typ całkowitoliczbowy odpowiadający typowi unsigned będę zapisywał w pliku jako unsigned_int. Chciałem wypróbować obsługę błędów. Jeśli jest błędny typ w pliku, to ma się wyświetlać proste okienko z informacją o błędzie.

Obsługa tego wygląda tak:

try
{
    //pobieranie informacji z pliku
}
catch (FilterExceptions::InvalidParameterTypeInFile &exc)
{
    ModalInformation dialog(GTKUtilities::ERROR) ;
    REPORT_VAR(exc.what()) ;  //linia 300
    dialog.setInformation(exc.what()) ;
    REPORT_VAR(exc.what()) ; //linia 302
    dialog.setTitle("Niepoprawny typ filtru") ;
    dialog.run() ;
}

FilterExceptions::InvalidParameterTypeInFile to klasa wyjątku, dziedziczy po std::exception

makra REPORT_VAR służą do zapisywania wartości zmiennych.

metody setInformation() i run() wyglądają tak:

void ModalInformation::setInformation(const char* information)
{
    REPORTER_F_THR(rep) ;
    message = information ;
    REPORT_VAR(information) ; //linia 15
    REPORT_VAR(message) ; //linia 16
}

void ModalInformation::run()
{
    REPORTER_F_THR(rep) ;
    REPORT_VAR(message) ; //linia 33
    GtkWidget *dialog = gtk_message_dialog_new(
        NULL,
        GTK_DIALOG_MODAL, 
        static_cast<GtkMessageType>(type),
        GTK_BUTTONS_OK,
        "%s", message.c_str()
    ) ;
    REPORT_VAR(message) ; //linia 41
    gtk_window_set_title(GTK_WINDOW(dialog), title.c_str()) ;
    gtk_dialog_run(GTK_DIALOG(dialog)) ;
    gtk_widget_destroy(dialog) ;    
}

ModalInformation::message jest polem klasy typu std::string

niby ok. nigdzie po drodze NIE SĄ niszczone obiekty przetrzymujące te zmienne.

log:

exc.what() == Invalid type: unsigned.       line 300 in filterSignal(), /d/studia/inzynierka/programy/EKGFilter/EKGFilter/EKGFilter/gtk_thread.cpp
information == Invalid type: unsign(        line 15 in setInformation(), /d/studia/inzynierka/programy/EKGFilter/EKGFilter/EKGFilter/gtk_my_dialogs.cpp
message == Invalid type: unsign(        line 16 in setInformation(), /d/studia/inzynierka/programy/EKGFilter/EKGFilter/EKGFilter/gtk_my_dialogs.cpp
exc.what() == Invalid type: unsigned.       line 302 in filterSignal(), /d/studia/inzynierka/programy/EKGFilter/EKGFilter/EKGFilter/gtk_thread.cpp
message == Invalid type: unsign(        line 33 in run(), /d/studia/inzynierka/programy/EKGFilter/EKGFilter/EKGFilter/gtk_my_dialogs.cpp
message == Invalid type: unsign(        line 41 in run(), /d/studia/inzynierka/programy/EKGFilter/EKGFilter/EKGFilter/gtk_my_dialogs.cpp

czemu do k. nędzy wywołanie exc.what() zwraca poprawną informację, a przekazane do settera zmiennej message już ulega uszkodzeniu??

0

Chodzi o to, że najpierw jest "unsigned", a później "unsign(" tak?

Widać, że coś pisze po pamięci tam gdzie nie powinno. Tak więc użyj debuggera. W takich sytuacjach mogą być przydatne watchpointy (inaczej zwane data breakpoints), czyli breakpoint założony na odczyt/zapis danej komórki pamięci. Zwykle rzadko się przydają, ale może akurat będą ci pomocne, jeśli nie możesz dojść w którym momencie zawartość łańcucha się zmienia:

  • ustaw sobie najpierw zwykły breakpoint w miejscu gdzie łańcuch jest jeszcze ok
  • odpal program, poczekaj aż się zatrzyma na breakpoincie
  • ustal jaki adres ma ten felerny zmieniający się znak "e" i dodaj ten adres jako watchpoint
  • wznów program i czekaj aż się zatrzyma na watchpoincie
0

ależ używam debuggera. a gdy używam go bardzo powoli, to nawet działa poprawnie. chodzi o to, że exc.what() przed przekazaniem się do setInformation() zwraca poprawny ciąg znaków, natomiast zaraz po wejściu do setInformation jest już inny

0

dodałem jednego stringa i działa:

    catch (FilterExceptions::InvalidParameterTypeInFile &exc)
    {
        ModalInformation dialog(GTKUtilities::ERROR) ;
        REPORT_VAR(exc.what()) ;
        std::string info = exc.what() ;
        REPORT_VAR(info) ;
        dialog.setInformation(info.c_str()) ;
        REPORT_VAR(exc.what()) ;
        REPORT_VAR(info) ;
        dialog.setTitle("Niepoprawny typ filtru") ;
        dialog.run() ;
    }

log:

exc.what() == Invalid type: unsigned.       line 300 in filterSignal(), /d/studia/inzynierka/programy/EKGFilter/EKGFilter/EKGFilter/gtk_thread.cpp
info == Invalid type: unsigned.     line 302 in filterSignal(), /d/studia/inzynierka/programy/EKGFilter/EKGFilter/EKGFilter/gtk_thread.cpp
information == Invalid type: unsigned.      line 15 in setInformation(), /d/studia/inzynierka/programy/EKGFilter/EKGFilter/EKGFilter/gtk_my_dialogs.cpp
message == Invalid type: unsigned.      line 16 in setInformation(), /d/studia/inzynierka/programy/EKGFilter/EKGFilter/EKGFilter/gtk_my_dialogs.cpp
exc.what() == Invalid type: unsigned.       line 304 in filterSignal(), /d/studia/inzynierka/programy/EKGFilter/EKGFilter/EKGFilter/gtk_thread.cpp
info == Invalid type: unsigned.     line 305 in filterSignal(), /d/studia/inzynierka/programy/EKGFilter/EKGFilter/EKGFilter/gtk_thread.cpp
message == Invalid type: unsigned.      line 33 in run(), /d/studia/inzynierka/programy/EKGFilter/EKGFilter/EKGFilter/gtk_my_dialogs.cpp
message == Invalid type: unsigned.      line 41 in run(), /d/studia/inzynierka/programy/EKGFilter/EKGFilter/EKGFilter/gtk_my_dialogs.cpp

jednak nadal nie mam pojęcia czemu poprzedni kod nie działał. jestem wręcz przekonany, że jest poprawny

0

obszedłem to. ale jakby komuś się chciało to rozkminiać, to może dam kod klasy wyjątku:

    class InvalidParameterTypeInFile: public std::exception
    {
        public:
            InvalidParameterTypeInFile(const char *parameterType)throw()
            {
                oss<<"Invalid type: "<<parameterType<<"." ;
                oss.flush() ;
            }
            virtual ~InvalidParameterTypeInFile()throw(){}
            InvalidParameterTypeInFile(const InvalidParameterTypeInFile&exc)
            {
                oss.str(exc.oss.str()) ;
            }

            virtual const char* what() const throw()
            {
                return oss.str().c_str() ;
            }
        private:
            std::ostringstream oss ;
    };
0

odkryłem bardzo interesującą rzecz w tym projekcie:

gdy przekazuję do funkcji/metody literał, czyli:

void Klasa::metoda(const char *tekst) ;

czasami 3 ostatnie znaki tego ciągu znaków są usuwane i nadpisywane znakiem '('

jak to działa. mam sobie setter:

inline void Filter::setFilterFilePath(const char* newFilterFilePath)
{
    REPORTER_F_THR(rep) ;
    filterFilePath = newFilterFilePath ;
}

niby nic. zwykły setter.

REPORTER_F_THR(x) jest to makro tworzące obiekt o nazwie x klasy ReporterThread. służy to do przejrzystego logowania do pliku ścieżki przejścia programu wraz z zapisem wybranych zmiennych do pliku, uwzględnia wielowątkowość:

#define REPORTER_F_THR(x) RROut::ReporterThread x((__func__),(__LINE__),(__FILE__), pthread_self())

(...)

    class ReporterThread
    {
        public:
            ReporterThread(const char *name, pthread_t threadId) ;
            ReporterThread(const char *funcName, int lineNumber,
                const char *fileName, pthread_t threadId) ;
            ~ReporterThread() ; //not virtual

            void lock() ;
            void unlock() ;

            static std::ofstream &getFile(pthread_t threadId) ;

            static void writeBranches(pthread_t threadId) ;

            static int printf(pthread_t threadId, const char *format, ...) ;
        protected:
            std::string getName()const ;
            const pthread_t myThreadId ;
            static std::map<pthread_t, unsigned> depth ;
            static std::map<pthread_t, std::ofstream*> files ;

            static void writeNewBranch(pthread_t threadId) ;
            static void writeEndBranch(pthread_t threadId) ;
        private:
            static bool deletedFiles ;
            const std::string nameOfBlock ;

            const std::string createNameOfBlock(const char *funcName, pthread_t threadId) ;
            const std::string createNameOfBlock(const char *funcName,
                int lineNumber, const char *fileName, pthread_t threadId) ;

            void constructorBody() ;
            void reportIfThreadStart() ;
            void reportIfThreadStop() ;

            static void checkDeletedFiles() ;
            const std::string getFileName();
            static const std::string getFileName(pthread_t threadId) ;
    };

czyli wywołanie tego makra w tym setterze po preprocessingu będzie wyglądać tak:

inline void Filter::setFilterFilePath(const char* newFilterFilePath)
{
    RROut::ReporterThread rep("setFilterFilePath", 132, "filter.h", 46327547) ;
    filterFilePath = newFilterFilePath ;
}

w pewnym momencie przesyłam ciąg znaków "filters/usredniaj.flr" do tego settera. przedstawiam kroki debuggowania:

1. Zatrzymujemy się na breakpoincie.
user image

2. Przechodzimy przez utworzenie zmiennej rep.
user image
I stało się coś nieoczekiwanego! Został nadpisany argument newFilterFilePath

Ostatni krok jest formalnością:
3.
user image

Teraz temu, kto mi wyjaśni czemu wywołanie tego makra zmieniło wartość argumentu, należy się piwo....

0

moja skromna wiedza w temacie c++ podpowiada, że coś się rozepchnęło na stosie, albo gdzieś w kodzie konstruktora klasy ReporterThread masz coś, co modyfikuje ten string. const uchroni przez zmianą wskaźnika, ale raczej nie przed zmianą zawartości danych, na które wskazuje.

0

Zjarek masz rację!

patrząc wgłąb kodu zobaczyłem, że ten const char* jest przesyłany przez funkcję, która właśnie zwraca taki tymczasowy std::string. jeśli nie zostanie przepisany do innego stringa od razu to lipa. no i właśnie lipa wynikła.

zmieniłem tą funkcję tak, aby zwracała referencję na std::string

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