strtok? WTF! Wer benutzt das?

Gast #7168317
Lesenswert?

Hallo :)
Ich wollte mich nie damit beschäftigen, habe es jetzt mal versucht und 
will es aber schon nicht mehr. Dennoch habe ich ein paar Fragen. Es geht 
um strtok().

Wer benutzt strtok() wirklich?
Wo wird der geheime Hintergrundstring geseichert?
Wie bzw wann wird dieser wieder freigegeben?
Und kann man soetwas qualifizieren?

Meinen Versuchen zu urteilen, kann man strtok nicht verschachteln bzw 
ihm neue delimiter geben, da dann die vorherigen Abhängigkeiten zerstört 
werden. Macht ja auch Sinn. Oder geht das doch irgendwie?

Viele Grüße und noch einen schönen Abend euch :)
Gast #7168361
Lesenswert?

MaWin schrieb:
> statisch

Das stimmt natürlich. Aber darauf ist es nicht schön Einfluss zu nehmen. 
Der Speicher wird ggf. mit einem langen String über die komplette 
Laufzeit zugemüllt. Da ja auch noch dynamisch allokiert wird.

strtok_r und es gibt auch strtok_s. Das ist schon bekannt. Konkret 
meinte ich hier strtok.
Gast #7168365
Lesenswert?

Dominic schrieb:
> MaWin schrieb:
>> statisch
>
> Das stimmt natürlich. Aber darauf ist es nicht schön Einfluss zu nehmen.
> Der Speicher wird ggf. mit einem langen String über die komplette
> Laufzeit zugemüllt. Da ja auch noch dynamisch allokiert wird.

Es wird nichts dynamisch allokiert. Der übergeben String wird 
modifiziert: Die Trennzeichen werden durch terminierende Nullen ersetzt.
(Firma: LinTech GmbH) #7168367
Lesenswert?

ich verstehe das Problem nicht? benutze das zum Parsen von NMEA-Messages 
und das läuft und läuft und läuft
ptr = strtok(line, ",*");
      if (ptr == NULL)
        return MSG_FAIL;
      strcpy_s(manufacturer, ptr);
      for (i = 1;i < 5;i++)
      {
        ptr = strtok(NULL, ",*");
        if (ptr == NULL)
          return MSG_FAIL;
        switch (i)
        {
        case 1: strcpy_s(msg_type, ptr);break;
        case 2: strcpy_s(msg_id, ptr);break;
        case 3: strcpy_s(msg_value, ptr);break;
        case 4: strcpy_s(msg_cs, ptr);break;
        default: return MSG_FAIL;
        }
      }
Gast #7168373
Lesenswert?

Dominic schrieb:
> Wer benutzt strtok() wirklich?

Wieder so einer der glaubt erst mal allen vor den Karren scheißen zu 
müssen. Das wird seit 50 Jahren verwendet. Ob du es glaubst oder nicht, 
es funktioniert.

> Wo wird der geheime Hintergrundstring geseichert?

Nirgendwo. Weil es keinen gibt. Es wird ein Pointer in den 
Originalstring gespeichert und der Originalstring wird zerleg. Daher ist 
das erste Argument von strtok() nicht const.

> Wie bzw wann wird dieser wieder freigegeben?

Wenn du ihn frei gibst - weil es dein Originalstring ist, nur inhaltlich 
ein bisschen verändert.

> Und kann man soetwas qualifizieren?

Da hast du aber ein schönen Wort gelernt, qualifizieren. Nur was soll es 
in diesem Zusammenhang bedeuten?

> Meinen Versuchen zu urteilen, kann man strtok nicht verschachteln bzw
> ihm neue delimiter geben,

Delimiter kann man bei jedem Aufruf von strtok(NULL, delim) wechseln. 
Verschachteln geht mit Schmerzen.

> da dann die vorherigen Abhängigkeiten zerstört
> werden.

Welche Abhängigkeiten?

> Macht ja auch Sinn. Oder geht das doch irgendwie?

Ja, geht irgendwie, ist aber scheiße. Man berechnet sich den internen 
State selber und speichert ihn extern. In etwa so:
1
char *s = ...; // string to parse
2
char *last = s + strlen(s); // pointer to string's terminating \0
3

4
while(s < last) {
5
   char *token = strtok(s, delim); // next token
6
   printf("%s\n", token);
7
   s = token + strlen(token) + 1; // start of next scan, 1 behind token end
8

9
   // Do some other work with strtok()
10
}

Aber weil das scheiße ist strtok_r().
Gast #7168409
Lesenswert?

Ich habe mich von den str* Routinen verabschiedet, benutze meist 
std::string.
Bei den str* Routinen muss man immer aufpassen, ob da auch wirklich die 
\0 dranhängt. z.B. strncpy() läßt das evtl weg.
Ist str* ist jetzt 40 Jahre alt. Damals war es ok, heute erwartet man 
etwas mehr Komfort.
#7168463
Lesenswert?

strtok() eignet sich sehr gut, wenn man nur wenig RAM hat, da man keine 
Kopie braucht.
Heutzutage hat man aber massig RAM, daher benutze ich sscanf() zum 
Zerlegen in einzelne Argumente. sscanf() kann auch die Anzahl der 
Zeichen begrenzen, d.h. man kann zuverlässig einen Pufferüberlauf 
verhindern.
Gast #7168493
Lesenswert?

Ich habe das kürzlich mal gebraucht. Ich musste einen env string 
aufsplitten, und es musste schnell gehen:
https://projects.dpa.li/git/?p=Y11.git;a=blob;f=Y11/src/log.c;h=ed312adbd5c70a73a8b4020473bc1b34e3420ff0;hb=refs/heads/WIP

Aber ich muss das mal ändern. Den env String so zu ändern könnte später 
nämlich irgendwann mal unangenehme Nebeneffekte haben... Strtok ist ein 
zweischneidiges Schwert.
#7168512
Lesenswert?

Ron-Hardy G. schrieb:
> ich verstehe das Problem nicht? benutze das zum Parsen von NMEA-Messages
> und das läuft und läuft und läuft

Niemand behauptet strtok würde nicht funktionieren.
Ich benutze es auch, sogar auf einem AVR zum primitiven Parsen von JSON.

Aber es ist halt ein Kind seiner Zeit.
Niemand der die komfortablen Alternativen aus C++, Java oder gar Python 
mit seinen ausdruckstarken Funktionen kennt würde ohne Not wieder 
Stringoperationen in C mit strtok und Konsorten machen.

Die Tatsache dass es einen internen Zustand gibt und der Ursprungsstring 
manipuliert wird ist zwar technisch elegant und den Umständen 
geschuldet, verwirrt Anfänger aber maximal.
Gast #7168533
Lesenswert?

Le X. schrieb:
> Niemand der die komfortablen Alternativen aus C++, Java oder gar Python
> mit seinen ausdruckstarken Funktionen kennt würde ohne Not wieder
> Stringoperationen in C mit strtok und Konsorten machen.

Was sollen diese verallgemeinernden Sprüche? Alles hat seine 
Berechtigung, auch heute noch. Selbst auf fetten Rechnern die TByte 
lange Inputdaten verarbeiten müssen lohnt es sich darüber nachzudenken, 
ob man Milliarden von dynamischen Speicheranforderungen/Freigaben macht 
wenn man für jeden Dreck einen std::string benutzt oder native 
Pointeroperationen bzw. die str* Funktionen benutzt. Da können ganz 
schnell Faktor 10 dazwischen liegen. Für 100Byte lange Eingaben ist das 
natürlich was komplett anderes.
#7168543
Lesenswert?

temp schrieb:
> Was sollen diese verallgemeinernden Sprüche?

Da ist nichts verallgemeinert.
Ich schrieb "ohne Not".
Nun wenn dein Scenario vorsieht dass du Terrabytes an Strings 
bearbeitest, innerhalb von Mikrosekunden, ja, dann tut es wohl Not 
strtok zu benutzen.

temp schrieb:
> Alles hat seine
> Berechtigung, auch heute noch
Ja klar hat es das auch heute noch. Hab doch geschrieben dass ich es 
selber benutze.

Komm mal runter.
Gast #7168626
Lesenswert?

temp schrieb:
> Le X. schrieb:
>
>> Niemand der die komfortablen Alternativen aus C++, Java oder gar Python
>> mit seinen ausdruckstarken Funktionen kennt würde ohne Not wieder
>> Stringoperationen in C mit strtok und Konsorten machen.
>
> Was sollen diese verallgemeinernden Sprüche? Alles hat seine
> Berechtigung, auch heute noch. Selbst auf fetten Rechnern die TByte
> lange Inputdaten verarbeiten müssen lohnt es sich darüber nachzudenken,
> ob man Milliarden von dynamischen Speicheranforderungen/Freigaben macht
> wenn man für jeden Dreck einen std::string benutzt oder native
> Pointeroperationen bzw. die str* Funktionen benutzt. Da können ganz
> schnell Faktor 10 dazwischen liegen. Für 100Byte lange Eingaben ist das
> natürlich was komplett anderes.

Und du meinst der g++ nimmt dann immer die schlechtest denkbare 
Implementierung?

In der Regel bekommst du eine passende Implementierung welche eine gute 
Performance hat, notfalls muss man eine Compiler Anweisung einbauen.

Bei 100TB sprechen wir über ganz andere Mechaniken, da würde ich schauen 
direkt aus der NVMe SSD/NIC die Daten passend zu verarbeiten, ggf. 
direkt ohne Umwege zur GPU/FPGA/ASIC leiten usw, aber hier bewegen wir 
uns in einem ganz anderen Bereich der Optimierungen, hier bedarf es 
genauer Kenntnisse der Zielsysteme. Aber das ist bei 99,9% aller 
Anwendungen einfach nur überflüssig. Stell dir vor jemand bindet CUDA 
für ein Hello World ein...

Und bei der Diskrepanz zwischen Aufgaben die Eingabemengen in dieser 
Größe haben vs. in der Realität vielleicht 128Bit Eingaben haben ist 
durchaus beindruckend wie manche meinen da optimieren zu müssen.

Hatte Mal einen der genau mit so einem String Kram rumoptimiert hat und 
ewig diskutiert hat wie effizient sein Schrott sei. Der entsprechende 
Snippet lief ein einziges Mal nicht einmal eine Millisekunde beim Start 
bei einer Anwendung die Stunden läuft. Hab es durch den Profiler laufen 
lassen, war nicht messbar. Was messbar war: dumme Meetings und schlechte 
Wartbarkeit.
Gast #7168680
Lesenswert?

Dominic schrieb:
> Ich wollte mich nie damit beschäftigen, habe es jetzt mal versucht und
> will es aber schon nicht mehr. Dennoch habe ich ein paar Fragen. Es geht
> um strtok().

Wozu die ganze Aufregung?
Wer sowas wie strtok benutzt, parst den String zweimal: einmal innerhalb 
von strtok und dann nochmal selber - es sei denn daß man nach strtok das 
Ergebnis wegschmeißt, ohne es anzuschauen.
Und du wolltest dich doch eigentlich garnicht damit befassen.

Irgendwie kommt mir das wieder mal wie eine Zombie-Diskussion vor. So 
ähnlich wie die immer mal wieder hier auftauchenden Beiträge "Wieso... 
...ist es damals so gekommen wie es gekommen ist?"

W.S.
Gast #7168682
Lesenswert?

Le X. schrieb:
> Die Tatsache dass es einen internen Zustand gibt und der Ursprungsstring
> manipuliert wird ist zwar technisch elegant und den Umständen
> geschuldet, verwirrt Anfänger aber maximal.
Bis man die Doku gelesen hat. Das sollte man machen, bevor man die 
Funktion benutzt.

Und technisch elegant ist strtok nur wirklich nicht.
Gast #7168690
Lesenswert?

Dominic schrieb:
> Wer benutzt strtok() wirklich?
> Wo wird der geheime Hintergrundstring geseichert?

Nachdem hier wieder ein ahnungsloser Psychopath seine Unkenntnis zum 
Besten gibt:

> von MaWin (Gast)22.08.2022 22:52

in einer threadfesten Library natürlich threadlokal. Und es ist kein 
String, sondern nur das eine Zeichen das im Originalstring gegen NUL 
'\0' gepatcht wurde.

Und die Funktion funktioniert sehr gut, quasi wie beschrieben.
Gast #7168699
Lesenswert?

MaWin schrieb:
> in einer threadfesten Library natürlich threadlokal. Und es ist kein
> String, sondern nur das eine Zeichen das im Originalstring gegen NUL
> '\0' gepatcht wurde.
Wie funktioniert das mit dem threadlokal?
1
"global":
2
char s = "abc xyz";
3

4
thread 1:
5
char *t = strtok(s, " ");
6

7
thread 2:
8
char *t = strtok(NULL, " ");
ist soweit ich weiß nach Standard erlaubt und das Ergebnis klar 
definiert, solange die zeitliche Reihenfolge geregelt ist.

Und mit "Zeichen" meinst du vermutlich Pointer auf Zeichen?
Gast #7168707
Lesenswert?

(prx) A. K. schrieb:
> mh schrieb:
>> Wie funktioniert das mit dem threadlokal?
>
> Auf den üblichen Plattformen gibt es einen Adressbereich, der in jedem
> Thread auf eigenen Speicher verweist. Der könnte dafür genutzt werden.
> https://en.wikipedia.org/wiki/Thread-local_storage
Und wie setzt man damit strtok korrekt um, sodass man strok aus mehreren 
Threads auf den gleichen String anwenden kann (mein mini Beispiel)?
Gast #7168713
Lesenswert?

DerEgon schrieb:
> PittyJ schrieb:
>> Ich habe mich von den str* Routinen verabschiedet, benutze meist
>> std::string.
>
> Das heißt, Du benutzt kein C. Dann würde ich auch keine
> strIrgendwas-Funktionen verwenden.

Ja.
Das Unterforum heisst hier "PC-Programmierung". Geht also um was 
größeres als einen AtMega.
Und ob ich im Makefile statt gcc nun g++ eintrage, ändert kaum etwas.
Nur kann ich damit dann auch ein paar praktische C++ Features benutzen.
#7168755
Lesenswert?

mh schrieb:
> Und wie setzt man damit strtok korrekt um, sodass man strok aus mehreren
> Threads auf den gleichen String anwenden kann (mein mini Beispiel)?

strok selber ist explizit als "nicht thread safe" markiert.

Implementiert ist es wie folgt:
1
char *
2
strtok (char *s, const char *delim)
3
{
4
  static char *olds;
5
  return __strtok_r (s, delim, &olds);
6
}

Wenn du unbedingt bei "strtok" bleiben willst, aber trotzdem 
Thread-Sicher sein möchtest, könntest du dir ein
1
char *
2
strtok_ts (char *s, const char *delim)
3
{
4
  static __thread char *olds;
5
  return __strtok_r (s, delim, &olds);
6
}

Basteln (__thread bewirkt ThreadLocalStorage für das static olds, GCC 
extension...)
#7168765
Lesenswert?

mh schrieb:
> Auch danach habe ich nicht gefragt.

Na und? Aber darauf habe ich hingewiesen.

Für den gleichen String aus verschiedenen Threads, wenn gleichzeitig 
noch andere Threads weitere Strings bearbeiten tut aber weder strtok 
oder strtok_ts.
Dann musst du strtok_r verwenden, und dich selber um den olds-Pointer 
kümmern.
Gast #7168890
Lesenswert?

Εrnst B. schrieb:
> mh schrieb:
>> Auch danach habe ich nicht gefragt.
> Na und? Aber darauf habe ich hingewiesen.
Warum zitierst du mich, wenn du nicht auf mich antwortest?

Ich habe das Beispiel von oben vervollständigt, falls noch irgendjemand 
erklären möchte, wie ein standardkonformes strtok mit thread_local 
implementiert wird.
1
#include <stdio.h>
2
#include <string.h> 
3
#include <threads.h>
4

5
char string[] = "Hallo Welt";
6

7
int func_1() {
8
    char const* tok = strtok(string, " ");          
9
    printf("func_1: %s\n", tok);
10

11
    return 0;
12
}
13

14
int func_2() {
15
    char const* tok = strtok(NULL, " ");         
16
    printf("func_2: %s\n", tok);
17

18
    return 0;
19
}
20

21
int main() { 
22
    thrd_t thr1;
23
    thrd_t thr2;   
24
    int res;
25

26
    thrd_create(&thr1, func_1, NULL);
27
    thrd_join(thr1, &res);                        
28

29
    thrd_create(&thr2, func_2, NULL);
30
    thrd_join(thr2, &res);
31

32
    return 0;
33
}
#7168907
Lesenswert?

Wenn ich das richtig verstehe redet Ihr aneinander vorbei

strtok modifiziert den String selbst.

Wenn man mit mehreren Threads auf die Tokens dieses einen globalen 
Strings zugreifen will, dann müsste man entweder im main das tokenizing 
machen und den Threads das token mitgeben, oder man würde jedem Thread 
eine Kopie des Strings mitgeben.

ein threadsafes strtok bedeutet nur dass strtok mit jeweils 
unterschiedlichen (threadlokalen) Strings aufgerufen sich nicht 
gegenseitig beeinflusst, indem die interne statische Variable 
threadlocal ist.
Damit kannst du aber nicht in mehreren Threads auf den SELBEN globalen 
String zugreifen.
Gast #7168914
Lesenswert?

Udo S. schrieb:
> Wenn ich das richtig verstehe redet Ihr aneinander vorbei
Möglich, aber spätestens das vollständige Beispiel sollte klar sein.

> strtok modifiziert den String selbst.
Ja, wie schon mehrfach in diesem Thread angemerkt wurde und wie es in 
der Doku steht.

> Wenn man mit mehreren Threads auf die Tokens dieses einen Strings
> zugreifen will, dann müsste man entweder im main das tokenizing machen
> und den Threads das token mitgeben, oder man würde jedem Thread eine
> Kopie des Strings mitgeben.
Worauf basiert diese Aussage? Der Standard (C17) sagt lediglich, dass 
strtok nicht sicher gegen Dataraces ist. In meinem Beispiel gibt es 
soweit ich weiß kein Datarace.

> ein threadsafes strtok bedeutet nur dass strtok mit jeweils
> unterschiedlichen (threadlokalen) Strings aufgerufen sich nicht
> gegenseitig beeinflusst, indem die interne statische Variable
> threadlocal ist.
> Damit kannst du aber nicht in mehreren Threads auf den SELBEN globalen
> String zugreifen.
Genau darum ging es mir. MaWin will strtok mit thread_local threadsafe 
machen. Ich möchte wissen wie das gehen soll für ein standardkonformes 
strtok.
Gast #7168921
Lesenswert?

Udo S. schrieb:
> mh schrieb:
>> In meinem Beispiel gibt es
>> soweit ich weiß kein Datarace.
> Kannst du garantieren wann welche der beiden Threads ausgeführt werden?
> Im Prinzip kann der erste Thread unterbrochen werden bevor das
> strtok(string, " ");
> ausgeführt wird und der zweite macht dann den
> strtok(NULL, " ");
> zuerst.
Die thrd_join stehen da nicht zum Spaß ...
#7168923
Lesenswert?

mh schrieb:
> MaWin will strtok mit thread_local threadsafe
> machen.

Ja wenn eine Funktion threadsave ist kann man trotzdem mit der Unheil 
anrichten wenn man sie in mehreren Threads auf die selben Daten 
loslässt.

Threadsave heisst erst mal nur dass man die Funktion in mehreren Threads 
mit jeweils eigenen Daten benutzen kann ohne dass da irgendwas sich 
gegenseitig beeinflusst.
Das kann die originale strtok nicht leisten, weil sie eine interne 
globale static Variable hat.
#7168927
Lesenswert?

mh schrieb:
> Die thrd_join stehen da nicht zum Spaß ...

Sorry übersehen, ich programmiere selten in C und da nicht 
multithreaded.

Aber das funktioniert so mit einer threadsave strtok nicht, weil strtok 
im 2. Thread die Info nicht mehr hat wo der aktuelle tokenzeiger ist.
Dein Beispiel würde nur mit dem nicht threadsaven strtok funktionieren.
Gast #7168930
Lesenswert?

Udo S. schrieb:
> Aber das funktioniert so mit einer threadsave strtok nicht, weil strtok
> im 2. Thread die Info nicht mehr hat wo der aktuelle tokenzeiger ist.
> Dein Beispiel würde nur mit dem nicht threadsaven strtok funktionieren.
Dann ist es aber kein strtok mehr. Dann ist es eine threadsafe Funktion 
die etwas ähnliches wie strtok macht. Genau deshalb habe ich ja 
gefragt wie strtok mit thread_local gehen soll.
#7168937
Lesenswert?

mh schrieb:
> Genau deshalb habe ich ja
> gefragt wie strtok mit thread_local gehen soll.

Aber das tut ja schon so, wie du in deinem Beispiel geschrieben hast.

Eben mit den Nebenbedingungen, dass du die Ausführungsreihenfolge der 
Threads kontrollieren musst (du also eigentlich garkeine Threads 
brauchst) und kein dritter Thread irgendwie gleichzeitig strtok 
verwenden darf.

Eben weil "strtok" kein Thread Local Storage verwendet, die interne 
static variable "olds" deswegen zwischen den Threads geteilt wird.

mit der "strtok_ts"-Variante geht das nicht mehr. Da kann jeder Thread 
nach Belieben strtokken, ohne dass ihm andere Threads dazwischenfunken.
Aber: genau diese (eigentlich positive) Eigenschaft bewirkt, dass dein 
absurdes, an den Haaren herbeigezogenes Beispiel nicht mehr laufen wird.
Gast #7168952
Lesenswert?

Εrnst B. schrieb:
> Eben weil "strtok" kein Thread Local Storage verwendet, die interne
> static variable "olds" deswegen zwischen den Threads geteilt wird.

Ich brauche keine Erklärung wie strtok oder seine _t, _ts oder _r 
Verwandten funktionieren. Ich weiß auch, dass das Beispiel nicht sehr 
realistisch ist. Es ist ein Beispiel. Dieses Beispiel sollte allerdings 
laut Standard funktionieren (wenn nicht, warum?)
Ich möchte wissen, wie das von MaWin vorgeschlagene strtok mit 
thread_local funktionieren soll.
#7168956
Lesenswert?

Ich verstehe nicht so ganz was du willst.
In deinem Beispiel bekommt der 2. Thread beim Aufruf das 2. Token. Das 
funktioniert nur deshalb weil du ihn synchronisiert hast und du dich 
darauf verlässt, dass strtok eben nicht threadsave ist, du also die 
Information vom 1. Thread im 2. Thread parat hast.
Ein threadsave strtok hat das natürlich nicht mehr. Dein Beispiel 
verlässt sich ja gerade auf den im Normalfall (mehrere Threads benutzen 
strtok mit unterschiedlichen Strings) falsche Verhalten des nicht 
threadsave strtok.
#7168987
Lesenswert?

mh schrieb:
> Ich möchte wissen, wie das von MaWin vorgeschlagene strtok mit
> thread_local funktionieren soll.

mit deinem Beispiel: Garnicht.
Was daran liegt, dass du dein Beispiel exakt danach ausgerichtet hast.

Dass die diversen MaWin-Clone keine allzu begnadeten C-Programmierer 
sind, ist bekannt. Musst dir nicht extra Mühe geben, um die 
bloßzustellen.
Gast #7168988
Lesenswert?

Udo S. schrieb:
> Ich verstehe nicht so ganz was du willst.
Das ist sehr offensichtlich. Musst du auch nicht. Ich warte einfach 
nocht etwas. Vielleicht findet sich ja jemand, der die Frage beantworten 
kann, oder vielleicht meldet sich MaWin ja.

Für die, die es nicht mitverfolgt haben, es geht im wesentlichen um die 
3 Beiträge:
Beitrag "Re: strtok? WTF! Wer benutzt das?"

Beitrag "Re: strtok? WTF! Wer benutzt das?"

Beitrag "Re: strtok? WTF! Wer benutzt das?"
Gast #7168998
Lesenswert?

mh schrieb:
> vielleicht meldet sich MaWin ja.

Ich empfehle einfach einen Blick in die Doku

https://docs.microsoft.com/de-de/cpp/c-runtime-library/reference/strtok-strtok-l-wcstok-wcstok-l-mbstok-mbstok-l?view=msvc-170

 Hinweis

Jede Funktion verwendet eine statische Variable eines lokalen Threads, 
um die Zeichenfolge in Token zu analysieren. Daher können mehrere 
Threads diese Funktionen gleichzeitig aufrufen.


Wer will, kann natürlich auch in strtok.c im Library-Sourcecodeordner 
gucken.

Die zu zerlegenden strings müssen natürlich auch threadlokal sein, z.B. 
vom heap stammen.
Gast #7169077
Lesenswert?

MaWin schrieb:
> mh schrieb:
>> vielleicht meldet sich MaWin ja.
> Ich empfehle einfach einen Blick in die Doku
Natürlich gibts keine Erklärung von dir ... Wie passt das mit dem 
Standard zusammen?

MaWin schrieb:
> Die zu zerlegenden strings müssen natürlich auch threadlokal sein, z.B.
> vom heap stammen.
Aus welchem Hut hast du diese Anforderung gezogen? Und warum steht in 
deinem Link nichts davon?
#7169233
Lesenswert?

Ron-Hardy G. schrieb:
> ich verstehe das Problem nicht? benutze das zum Parsen von NMEA-Messages
> und das läuft und läuft und läuft

Ich konnte es damals nicht für NMEA verwenden und hab mir ein eigenes 
my_strtok geschrieben, das einen Tick anders funktioniert. Bei NMEA sind 
Felder, für die es gerade keinen Wert gibt, einfach leer. strtok liefert 
aber blöderweise, wenn zwei Delimiter direkt nacheinander kommen, keinen 
leeren String zurück, sondern überspringt den dann einfach. Für a,,b 
liefert strtok dann nur zwei Token, nämlich a und b. Das leere in der 
Mitte wird ignoriert.

mh schrieb:
> Ich brauche keine Erklärung wie strtok oder seine _t, _ts oder _r
> Verwandten funktionieren. Ich weiß auch, dass das Beispiel nicht sehr
> realistisch ist. Es ist ein Beispiel. Dieses Beispiel sollte allerdings
> laut Standard funktionieren (wenn nicht, warum?)

Wo steht denn im Standard, dass strtok kein thread-local storage 
verwenden darf?
Gast #7169327
Lesenswert?

Rolf M. schrieb:
> mh schrieb:
>> Ich brauche keine Erklärung wie strtok oder seine _t, _ts oder _r
>> Verwandten funktionieren. Ich weiß auch, dass das Beispiel nicht sehr
>> realistisch ist. Es ist ein Beispiel. Dieses Beispiel sollte allerdings
>> laut Standard funktionieren (wenn nicht, warum?)
>
> Wo steht denn im Standard, dass strtok kein thread-local storage
> verwenden darf?

Ich habe (mehrfach) gefragt, wie das im Standard beschriebene Verhalten 
implementiert werden kann, wenn thread_local verwendet wird. Ich habe zu 
keinem Zeitpunkt geschrieben, dass thread_local verboten ist.
#7169493
Lesenswert?

mh schrieb:
> Ich habe zu keinem Zeitpunkt geschrieben, dass thread_local verboten ist.

Du hast es aber impliziert:

> Dieses Beispiel sollte allerdings laut Standard funktionieren (wenn
> nicht, warum?)

Du sagst, dass der Standard verlangt, dass das funktioniert, also dass 
kein thread-local storage genutzt wird, denn nur so kann es 
funktionieren. Aber das verlangt der Standard gar nicht.
Ich glaube, hier ist irgendwie niemandem klar, welches Problem du 
siehst.
Gast #7169496
Lesenswert?

Rolf M. schrieb:
> Aber das verlangt der Standard gar nicht.

Genau. Der C-Standard kennt Threads überhaupt erst seit C11.

Hier https://en.cppreference.com/w/cpp/string/byte/strtok steht es auch 
explizit, wie man annehmen soll, dass die Storage implementiert ist.

Was MS da macht ist halt eine Auslegung des Standards, die vor C11 auf 
jeden Fall legitim war. Und auch heute noch eine vernünftige Sache ist.
#7169534
Lesenswert?

MaWin schrieb:
> Rolf M. schrieb:
>> Aber das verlangt der Standard gar nicht.
>
> Genau. Der C-Standard kennt Threads überhaupt erst seit C11.
> Hier https://en.cppreference.com/w/cpp/string/byte/strtok steht es auch
> explizit, wie man annehmen soll, dass die Storage implementiert ist.

Für die Standardkonformität ist nur entscheidend, was im Standard steht, 
und dort (bzw. dessen latest C17-draft) finde ich folgende Passagen:

"The functions in the standard library are not guaranteed to be 
reentrant and may modify objects with static or thread storage 
duration."

Und:

"The strtok function is not required to avoid data races with other 
calls to the strtok function."

Ich verstehe das so, dass strtok thread-safe und ggf. auch mit 
thread-local storage implementiert sein darf, aber nicht zwingend muss.
Gast #7169620
Lesenswert?

Rolf M. schrieb:
> [...]

Ok, ich versuche es anders. Wie ist definiert, was strtok macht 
(Parameter, Rückgabewert, Vor- Und Nachbedingungen)? Ich würde sagen, 
dass das Verhalten durch den Text im Standard im Kapitel "7.24.5.7 The 
strstr function" definiert wird. Und wenn ich diesen Text lese, sehe ich 
nicht, warum mein Beispiel nicht funktionieren sollte.

Der Satz
1
The strtok function is not required to avoid data races with other calls to the strtok function.
der in eben diesem Kapitel steht, ist für das Beispiel nicht relevant, 
da es keine data races geben kann.

Und der Satz
1
The functions in the standard library are not guaranteed to be
2
reentrant and may modify objects with static or thread storage
3
duration.
ändert nichts, da kein reentrant stattfindet. Und den Satz darf man auch 
nicht lesen als "Every function in the standard library may modify 
objects with static or thread storage duration"
Gast #7169648
Lesenswert?

mh schrieb:
> Beispiel nicht relevant, da es keine data races geben kann.

Wieso eigentlich nicht? C garantiert dir mW nicht, dass die static 
variable zwischen den Threads direkt synchronisiert wird. Da kann 
beliebig etwas über die Caches Race conditions erzeugen. Besonders wenn 
jemand eine besonders bösartige Architektur entwerfen sollte.
Gast #7169682
Lesenswert?

Den Sourcecode zu strtok_r habe ich z.B. hier gefunden.
https://code.woboq.org/userspace/glibc/string/strtok_r.c.html

Wem das Original nicht gefällt, der kann die 35-Zeilen lange Funktion 
für sich auch einfach ändern. Fällt ja noch unter Fingerübung.

Interessant auch die 'normale' strtok Funktion.
https://code.woboq.org/userspace/glibc/string/strtok.c.html
Diese ruft intern einfach die _r Variante auf.

char *
strtok (char *s, const char *delim)
{
  static char *olds;
  return __strtok_r (s, delim, &olds);
}
Gast #7170318
Lesenswert?

avr schrieb:
> mh schrieb:
>> Beispiel nicht relevant, da es keine data races geben kann.
> Wieso eigentlich nicht? C garantiert dir mW nicht, dass die static
> variable zwischen den Threads direkt synchronisiert wird. Da kann
> beliebig etwas über die Caches Race conditions erzeugen. Besonders wenn
> jemand eine besonders bösartige Architektur entwerfen sollte.

Das wird durch den Standard geregelt. Wer hättes das gedacht?
Um das selbst nachvollziehen zu können, musst du das Kapitel 
"Environment/Conceptual models/Execution environments/Multi-threaded 
executions and data races lesen". Es ist drei Seiten lang, eine Seite 
ist hier relevant. Der Rest beschäftigt sich mit atomics. Danach kannst 
du die Beschreibung der drei vorkommenden Funktionen (strtok, thrd_join, 
thrd_create) lesen. Wie einer der MaWins geschrieben hat, thrd_join ist 
die Lösung.
#7170608
Lesenswert?

mh schrieb:
> Wie einer der MaWins geschrieben hat, thrd_join ist
> die Lösung.

Nicht wirklich.
Bei der glibc teilen sich die Threads den strtok-state, bei der 
Microsoft-Implementierung hat jeder Thread seinen eigenen State.

mit dem thrd_join kriegst du dein Beispiel so hingemogelt, dass es mit 
der glibc in der aktuellen Version das tut, was du erwartest.
Mit einer anderen libc muss das nicht funktionieren, und auch mit einer 
zukünftigen glibc-Version nicht...

"die Lösung" ist "strtok_r", da kannst du selber festlegen, welcher 
Thread wann welchen State benutzt.
Gast #7170704
Lesenswert?

Εrnst B. schrieb:
> mh schrieb:
>> Wie einer der MaWins geschrieben hat, thrd_join ist
>> die Lösung.
>
> Nicht wirklich.
> Bei der glibc teilen sich die Threads den strtok-state, bei der
> Microsoft-Implementierung hat jeder Thread seinen eigenen State.
Hast du zufällig die Frage gelesen, auf die ich mit meinem Beitrag 
geantwortet habe? Weil deine Antwort ergibt in dem Zusammenhang keinen 
Sinn.

> mit dem thrd_join kriegst du dein Beispiel so hingemogelt, dass es mit
> der glibc in der aktuellen Version das tut, was du erwartest.
Was ist hier gemogelt? Was erwarte ich, das der Standard nicht 
beschreibt?

> Mit einer anderen libc muss das nicht funktionieren, und auch mit einer
> zukünftigen glibc-Version nicht...
Wenn sie standardkonform ist, muss es funktionieren. Alternativ kannst 
du zeigen, wo mein Beispiel gegen den Standard verstößt.
#7170935
Lesenswert?

mh schrieb:
> Ok, ich versuche es anders. Wie ist definiert, was strtok macht
> (Parameter, Rückgabewert, Vor- Und Nachbedingungen)? Ich würde sagen,
> dass das Verhalten durch den Text im Standard im Kapitel "7.24.5.7 The
> strstr function" definiert wird. Und wenn ich diesen Text lese, sehe ich
> nicht, warum mein Beispiel nicht funktionieren sollte.

Kommt halt drauf an, wie man den Begriff "sequence" zu verstehen hat:
"A sequence of calls to the strtok function breaks the string pointed to 
by s1 into a sequence of tokens". Der Standard scheint aber nicht zu 
definieren, was er mit "sequence of calls" meint.
Ist eine "sequence of calls" etwas, das komplett in einem Thread läuft, 
oder kann die auch auf mehrere Threads verteilt werden? Das einzige, was 
grob in die Richtung geht, ist die Definition des Begriffs "sequenced 
before", die sich explizit nur auf einen einzelnen Thread bezieht:
"Sequenced before is an asymmetric, transitive, pair-wise relation 
between evaluations executed by a single thread, which induces a partial 
order among those evaluations."
Gast #7170966
Lesenswert?

sequence -> nachfolgend. Wenn nicht gesagt wird "aber nur im selben 
thread nachfolgend", dann finde ich ist das ziemlich klar, dass der 
Thread dabei egal ist.

Wenn ich sage die Wand ist Rot, dann meine ich auch nicht, Wand ist Rot 
während dem Sonnenuntergang. Man kann nicht einfach Zusatzanforderungen 
anfügen. Das ändert die Bedeutung immer komplett.
#7171036
Lesenswert?

DPA schrieb:
> sequence -> nachfolgend.

sequence = zusammengehörende Reihe.

> Wenn ich sage die Wand ist Rot, dann meine ich auch nicht, Wand ist Rot
> während dem Sonnenuntergang. Man kann nicht einfach Zusatzanforderungen
> anfügen. Das ändert die Bedeutung immer komplett.

Also du denkst, dass "sequence" an der Stelle in komplett anderer 
Bedeutung verwendet wird als in dem Begriff "sequenced before"? Denn da 
steht ja, dass "sequenced before" bedeutet, dass alles in einem Thread 
ist. Das liefe dann ungefähr darauf hinaus: "wenn ich sage, die Wand ist 
rot, dann ist sie rot, aber wenn ich sage, die Tür ist rot, dann ist sie 
in Wirklichkeit grün."
Gast #7171051
Lesenswert?

Rolf M. schrieb:
> DPA schrieb:
>> sequence -> nachfolgend.
>
> sequence = zusammengehörende Reihe.
>
>> Wenn ich sage die Wand ist Rot, dann meine ich auch nicht, Wand ist Rot
>> während dem Sonnenuntergang. Man kann nicht einfach Zusatzanforderungen
>> anfügen. Das ändert die Bedeutung immer komplett.
>
> Also du denkst, dass "sequence" an der Stelle in komplett anderer
> Bedeutung verwendet wird als in dem Begriff "sequenced before"? Denn da
> steht ja, dass "sequenced before" bedeutet, dass alles in einem Thread
> ist. Das liefe dann ungefähr darauf hinaus: "wenn ich sage, die Wand ist
> rot, dann ist sie rot, aber wenn ich sage, die Tür ist rot, dann ist sie
> in Wirklichkeit grün."

Hat das "sequence of tokens" im gleichen Satz etwas mit "sequenced 
before" zu tun? Wenn "sequenced before" gemeint ist, steht auch 
"sequenced before" im Text. Oder warum sollte man davon ausgehen, dass 
ausgerechnet bei strtok auf die korrekte Sprache verzichtet wurde?
Gast #7171089
Lesenswert?

Ich sehe da auch keinen Zusammenhang zwischen dem "sequenced before" und 
dem "sequence of calls". Im "sequenced before" geht es um wenn A vor B 
passiert, als ein shortcut, um klar zu sagen was da jeweils damit 
gemeint ist, wenn effektiv "sequenced before" steht. Uhne das könnte man 
das ganze Zeugs mit Sequenzpunkten vermutlich nur schwer festlegen.
Aber beim Satz mit "sequence of calls", geht es nur darum, dass die 
Funktionsaufrufe zusammengehören, man eine "Folge von Funktionsaufrufen" 
hat, die sich beeinflussen / zusammengehören. Ein komplett anderer 
Kontext, und ein komplett anderer Zweck. Ich sehe nicht, wie das in 
irgendeinsterweise etwas zusätzliches über diese Aufrufe aussagen 
sollte.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren