seltsames strtok_s problem
-
Hallo zusammen,
hab die strtok_s funktion eingesetzt die sich für mein vorhaben sehr gut eignet. Dennoch hat sie schein bar ein problem:
DLLEXPORT bool EnumExtensions(EXTENSIONS *extensions) { [...] char *token; char *ext_string = (char*)glGetString(GL_EXTENSIONS); char *next_token; token = strtok_s(ext_string, " ", &next_token); while(token != NULL) { [...] token = strtok_s(NULL, " ", &next_token); } return true; }
Beim ersten aufruf der Funktion werden alle Extensions angezeigt und auch geparst. Wenn ich jedoch die Funktion ein zweites mal aufrufe findet er nur die erste Extension, next token hat von anfang nichts mehr drinstehen. Irgendwie steh ich da grad im Walde.
-
Woher stammt denn die strtok**_s**? Ich kenne nur man: strtok - und die zerhackt den Eingabestring als Nebeneffekt in handliche Stücke, indem sie die gefundenen Trennzeichen mit '\0' überschreibt.
(wenn das ein Problem ist, könntest du per strchr() oder strnspn() die Trennzeichen suchen und die Teilstrings von Hand rauskopieren)
-
strtok_s ist die secure version von strtok. Und außerdem kann sie mit konstanten strings umgehen =))
strtok ist doch mittlerweile veraltet ^^Find the next token in a string, using the current locale or a locale passed in. These are versions of strtok, _strtok_l, wcstok, _wcstok_l, _mbstok, _mbstok_l with security enhancements as described in Security Enhancements in the CRT. char *strtok_s( char *strToken, const char *strDelimit, char **context ); char *_strtok_s_l( char *strToken, const char *strDelimit, char **context, _locale_t locale ); wchar_t *wcstok_s( wchar_t *strToken, const wchar_t *strDelimit, wchar_t **context ); wchar_t *_wcstok_s_l( wchar_t *strToken, const wchar_t *strDelimit, wchar_t **context, _locale_t locale ); unsigned char *_mbstok_s( unsigned char*strToken, const unsigned char *strDelimit, char **context ); unsigned char *_mbstok_s( unsigned char*strToken, const unsigned char *strDelimit, char **context, _locale_t locale ); Parameters strToken String containing token or tokens. strDelimit Set of delimiter characters. context Used to store position information between calls to strtok_s locale Locale to use. Return Value Returns a pointer to the next token found in strToken. They return NULL when no more tokens are found. Each call modifies strToken by substituting a NULL character for each delimiter that is encountered. Error Conditions strToken strDelimit context Return value errno NULL any pointer to a null pointer NULL EINVAL any NULL any NULL EINVAL any any NULL NULL EINVAL If strToken is NULL but context is a pointer to a valid context pointer, there is no error. Remarks The strtok_s function finds the next token in strToken. The set of characters in strDelimit specifies possible delimiters of the token to be found in strToken on the current call. wcstok_s and _mbstok_s are wide-character and multibyte-character versions of strtok_s. The arguments and return values of wcstok_s and _wcstok_s_l are wide-character strings; those of _mbstok_s and _mbstok_s_l are multibyte-character strings. These three functions behave identically otherwise. This function validates its parameters. If an error condition occurs, as in the Error Conditions table, the invalid parameter handler is invoked, as described in Parameter Validation. If execution is allowed to continue, these functions set errno to EINVAL and return NULL. Generic-Text Routine Mappings TCHAR.H routine _UNICODE & _MBCS not defined _MBCS defined _UNICODE defined _tcstok_s strtok_s _mbstok_s wcstok_s _tcstok_s_l _strtok_s_l _mbstok_s_l _wcstok_s_l On the first call to strtok_s the function skips leading delimiters and returns a pointer to the first token in strToken, terminating the token with a null character. More tokens can be broken out of the remainder of strToken by a series of calls to strtok_s. Each call to strtok_s modifies strToken by inserting a null character after the token returned by that call. The context pointer keeps track of which string is being read and where in the string the next token is to be read. To read the next token from strToken, call strtok_s with a NULL value for the strToken argument, and pass the same context parameter. The NULL strToken argument causes strtok_s to search for the next token in the modified strToken. The strDelimit argument can take any value from one call to the next so that the set of delimiters may vary. Since the context parameter supersedes the static buffers used in strtok and _strtok_l, it is possible to parse two strings simultaneously in the same thread. The output value is affected by the setting of the LC_CTYPE category setting of the locale; see setlocale for more information. The versions of these functions without the _l suffix use the current locale for this locale-dependent behavior; the versions with the _l suffix are identical except that they use the locale parameter passed in instead. Requirements Routine Required header Compatibility strtok_s <string.h> Windows 95, Windows 98, Windows 98 Second Edition, Windows Millennium Edition, Windows NT 4.0, Windows 2000, Windows XP Home Edition, Windows XP Professional, Windows Server 2003 _strtok_s_l <string.h> Windows 95, Windows 98, Windows 98 Second Edition, Windows Millennium Edition, Windows NT 4.0, Windows 2000, Windows XP Home Edition, Windows XP Professional, Windows Server 2003 wcstok_s, _wcstok_s_l <string.h> or <wchar.h> Windows 95, Windows 98, Windows 98 Second Edition, Windows Millennium Edition, Windows NT 4.0, Windows 2000, Windows XP Home Edition, Windows XP Professional, Windows Server 2003 _mbstok_s, _mbstok_s_l <mbstring.h> Windows 95, Windows 98, Windows 98 Second Edition, Windows Millennium Edition, Windows NT 4.0, Windows 2000, Windows XP Home Edition, Windows XP Professional, Windows Server 2003 For additional compatibility information, see Compatibility in the Introduction. Example Copy Code // crt_strtok_s.c // In this program, a loop uses strtok_s // to print all the tokens (separated by commas // or blanks) in two strings at the same time. // #include <string.h> #include <stdio.h> char string1[] = "A string\tof ,,tokens\nand some more tokens"; char string2[] = "Another string\n\tparsed at the same time."; char seps[] = " ,\t\n"; char *token1, *token2, *next_token1, *next_token2; int main( void ) { printf( "Tokens:\n" ); // Establish string and get the first token: token1 = strtok_s( string1, seps, &next_token1); token2 = strtok_s ( string2, seps, &next_token2); // While there are tokens in "string1" or "string2" while ((token1 != NULL) || (token2 != NULL)) { // Get next token: if (token1 != NULL) { printf( " %s\n", token1 ); token1 = strtok_s( NULL, seps, &next_token1); } if (token2 != NULL) { printf(" %s\n", token2 ); token2 = strtok_s (NULL, seps, &next_token2); } } } Output Tokens: A Another string string of parsed tokens at and the some same more time. tokens
-
Aber offenbar ist es nicht SOO sicher, wie du es brauchst:
virtualdreams-offline schrieb:
Return Value
Returns a pointer to the next token found in strToken. They return NULL when no more tokens are found. Each call modifies strToken by substituting a NULL character for each delimiter that is encountered.^^ Dieses Verhalten hat strtok_s offenbar von strtok geerbt
-
Hmm, aber es werden doch alle variablen neu angelegt beim funktionsaufruf, also müßte es rein theoretisch auch wieder so funktionieren. Zumal kann strToken gar nicht modifiziert werden da er wegen glGetString() const ist ??? oder täusch ich mich da?
-
VirtualDreams schrieb:
Hmm, aber es werden doch alle variablen neu angelegt beim funktionsaufruf, also müßte es rein theoretisch auch wieder so funktionieren. Zumal kann strToken gar nicht modifiziert werden da er wegen glGetString() const ist ??? oder täusch ich mich da?
Ja, da täuschst du dich vermutlich - durch deinen Cast hast du die const-ness von strToken explizit aufgehoben (und weil dein Compiler dir vertraut, beschwert er sich auch nicht darüber). Und ob die Variablen wirklich neu angelegt werden, bin ich mir nicht sicher - womöglich liefert glGetString() nur einen Zeiger auf deinen String und dann arbeitet jeder Aufruf auf dem selben Datenbereich.
(aber wie das läuft, könntest du dir mal im Debugger ansehen)
-
Du hast recht, im Debugger wird der ext_string so kaputt gemacht das nur noch die erste extension drinsteht. Wird die Funktion erneut aufgerufen schreibt er lediglich nur noch die erste ext. rein. Scheint also wirklich auf dem selben speicher zu laufen.
Wie kann man nun das problem beheben. glGetString liefert in jedem Fall einen konstanten string zurück.
-
Wie wärs mit Anlegen einer nicht-konstanten Kopie?
-
Hat ich auch schon mal vor, aber wieder verworfen ^^ Werd wohl aber nicht drumrum kommen ^^
-
VirtualDreams schrieb:
Wie kann man nun das problem beheben. glGetString liefert in jedem Fall einen konstanten string zurück.
Und da siehst du, warum Cast's so gefährlich sind - durch das '**(char*)**glGetString(...)' sagst du dem Compiler, daß du die Rückgabe als nicht-konstanten String verwenden willst. Also beschwert er sich nicht mehr, daß er eigentlich nicht darauf schreiben dürfte.
(und je nachdem, wohin der zurückgegebene Zeiger verweist, bekommst du entweder eine Access Violation zur Laufzeit oder - wie in deinem Fall - schreibst munter in Bereiche, die von der Programmlogik nicht beschrieben werden sollten)
Als Lösung könntest du entweder eine lokale Kopie des Strings anlegen oder du verwendest andere str..() Funktionen, um den Extension-String zu zerteilen. (oder gleich std::string)
-
hab es jetzt mit
char *token; char *ext_string = new char[strlen((char*)glGetString(GL_EXTENSIONS)) + 1]; strcpy(ext_string, (char*)glGetString(GL_EXTENSIONS)); char *next_token; token = strtok_s(ext_string, " ", &next_token); [...] if(ext_string != NULL) delete[] ext_string;
gelöst und funktioniert wunderbar =))
-
Und jetzt noch die beiden Casts ((char*)glGetString) rausgenommen, dann wird es richtig gut
-
Das geht leider nicht da der compiler wegen GLubyte und char meckert ^^ Kann es nicht konvertieren.
-
Dann caste wenigstens nach const char* und nimm nen C++-Cast
-
OK =)) Zu Befehl ^^