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 ^^


Log in to reply