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