Forum: PC-Programmierung strtok? WTF! Wer benutzt das?


von Dominic (Gast)


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 :)

: Verschoben durch Admin
von Glibc (Gast)


Lesenswert?

Schau dir mal strtok_r an.

von MaWin (Gast)


Lesenswert?

Dominic schrieb:
> Wo wird der geheime Hintergrundstring geseichert?

statisch

Siehe strtok_r

von mehrmals nutzen? (Gast)


Lesenswert?

Die alten delimiter werden zu '\0' und der Originalstring wird 
überschrieben.

von Dominic (Gast)


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.

von avr (Gast)


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.

von Ron-Hardy G. (ron-hardy)


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;
        }
      }

von Wirklich? (Gast)


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().

von PittyJ (Gast)


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.

von Peter D. (peda)


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.

von STK500-Besitzer (Gast)


Lesenswert?

Peter D. schrieb:
> strtok() eignet sich sehr gut, wenn man nur wenig RAM hat, da man keine
> Kopie braucht.

und auch, wenn man das Original nicht mehr braucht.

von DPA (Gast)


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.

von Le X. (lex_91)


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.

von temp (Gast)


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.

von Le X. (lex_91)


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.

: Bearbeitet durch User
von DerEgon (Gast)


Lesenswert?

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.

von Onkel Ted (Gast)


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.

von W.S. (Gast)


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.

von mh (Gast)


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.

von Udo S. (urschmitt)


Lesenswert?

Onkel Ted schrieb:
> Hatte Mal einen der genau mit so einem String Kram rumoptimiert hat und
> ewig diskutiert hat wie effizient sein Schrott sei.
> ...

So ist es. Siehe dazu auch:
https://www.oreilly.com/library/view/c-coding-standards/0321113586/ch09.html

: Bearbeitet durch User
von MaWin (Gast)


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.

von mh (Gast)


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?

von (prx) A. K. (prx)


Lesenswert?

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

: Bearbeitet durch User
von mh (Gast)


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)?

von PittyJ (Gast)


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.

von Εrnst B. (ernst)


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...)

von mh (Gast)


Lesenswert?

Εrnst B. schrieb:
> 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.
Auch danach habe ich nicht gefragt.

von Εrnst B. (ernst)


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.

von mh (Gast)


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
}

von Udo S. (urschmitt)


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.

: Bearbeitet durch User
von mh (Gast)


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.

von Udo S. (urschmitt)


Lesenswert?

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.

von mh (Gast)


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ß ...

von Udo S. (urschmitt)


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.

von Udo S. (urschmitt)


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.

von mh (Gast)


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.

von Εrnst B. (ernst)


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.

: Bearbeitet durch User
von mh (Gast)


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.

von Udo S. (urschmitt)


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.

von Εrnst B. (ernst)


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.

von mh (Gast)


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?"

von MaWin (Gast)


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.

von mh (Gast)


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?

von MaWin (Gast)


Lesenswert?

mh schrieb:
> Aus welchem Hut hast du diese Anforderung gezogen?

Den, der meinen Kopf enthält. Ist bei dir nichts drin ?

> Und warum steht in deinem Link nichts davon?

Intelligenz ist durch nichts zu ersetzen

von MaWin (Gast)


Lesenswert?

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

Gar nicht, weil der Psychopath mal wieder dummes Zeug gelabert hat.

MaWin schrieb:
> Ich empfehle einfach einen Blick in die Doku
>
> https://docs.microsoft.com/

Das ist eine MS-spezifische Erweiterung, mein lieber Psychomawin.

von Rolf M. (rmagnus)


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?

von mh (Gast)


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.

von Rolf M. (rmagnus)


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.

: Bearbeitet durch User
von MaWin (Gast)


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.

von MaWin (Gast)


Lesenswert?

Oh, falscher Link, kommt aber aufs gleiche raus:
https://en.cppreference.com/w/c/string/byte/strtok

von Rolf M. (rmagnus)


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.

von mh (Gast)


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"

von mh (Gast)


Lesenswert?

Meinte natürlich "7.24.5.8 The strtok function"

von avr (Gast)


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.

von PittyJ (Gast)


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);
}

von MaWin (Gast)


Lesenswert?

avr schrieb:
> Wieso eigentlich nicht? C garantiert dir mW nicht, dass die static
> variable zwischen den Threads direkt synchronisiert wird.

thrd_join garantiert das.

von mh (Gast)


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.

von Εrnst B. (ernst)


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.

von mh (Gast)


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.

von DPA (Gast)


Lesenswert?

Windowser können halt nicht programmieren, und dann musste MS strtok 
notgedrungen falsch implementieren, damit die Programme nicht 
abschmieren.

von Rolf M. (rmagnus)


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."

: Bearbeitet durch User
von DPA (Gast)


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.

von Rolf M. (rmagnus)


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."

von mh (Gast)


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?

von DPA (Gast)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.