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
Die alten delimiter werden zu '\0' und der Originalstring wird überschrieben.
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.
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.
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; } }
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().
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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
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.
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?
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
(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)?
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.
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...)
Ε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.
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.
Ε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 | }
|
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
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.
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.
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ß ...
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.
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.
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.
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
Ε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.
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.
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.
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?"
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.
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?
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
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.
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?
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.
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
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.
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.
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"
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.
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); }
avr schrieb: > Wieso eigentlich nicht? C garantiert dir mW nicht, dass die static > variable zwischen den Threads direkt synchronisiert wird. thrd_join garantiert das.
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.
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.
Ε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.
Windowser können halt nicht programmieren, und dann musste MS strtok notgedrungen falsch implementieren, damit die Programme nicht abschmieren.
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
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.
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."
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?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.