Code für Ausgabe vereinfachen.
-
Pigeon schrieb:
Du könntest ein template schreiben [...] Und dies deiner Funktion mit Variabler Anzahl von parametern übergeben:
template<typename T> std::string str(T typ); // ... print(int Anzahl, ... ) { // Hier wie printf arbeiten, nur dass der Typ der Argumente // (string) bekannt ist }Das mit den variadischen Funktionen funktioniert nur für PODs (plain old data structures). std::string ist kein POD. Dieses Sprachmerkmal ("...") ist sooo unattraktiv, dass ich gar nicht weiß, wie man es richtig benutzen müsste. :p
Gruß,
SP
-
Sebastian Pizer schrieb:
Das mit den variadischen Funktionen funktioniert nur für PODs (plain old data structures). std::string ist kein POD. Dieses Sprachmerkmal ("...") ist sooo unattraktiv, dass ich gar nicht weiß, wie man es richtig benutzen müsste. :p
Gruß,
SP
Wieder was gelernt. 
-
Pigeon schrieb:
//Edit: Audacia macht es natürlich sehr viel schöner.

Mach dir nix draus, auf den Code von Audacia wäre ich auch in meinen kühnsten Träumen nicht gekommen!
Vor allem nicht auf die Zeilen:retval.resize (vsnprintf (0, 0, format, args)); vsnprintf (&retval[0], retval.size () + 1, format, args); :open_mouth:cout << str_printf ( "%s%d%s\n", "str_printf ist ", 1, "A!" );
@Sebastian Pizer
Ebenfalls ne 1A Variante, die ich (noch nicht ganz) kapiere aber die ich so benutzen kann:cout << (s = a2str() << "Meine " << 6 << " Frauen sind super!") ;
Pigeon schrieb:
Wieder was gelernt. 
Warum machst du so ein Gesicht? Freu dich doch, das du dazugelernt hast.

Das nächste mal bist du derjenige, der die beste Lösung vorschlägt.
Würde ich jedes mal so ein Gesicht machen, wenn ich etwas dazulerne, wäre mein Kinn schon blutig gescheuert vom auf dem Boden schleifen. :pSo, ich muss jetzt erstmal leider Feierabend machen und ab gehts zum Baden in die Sonne!
Vielen Dank allen beteiligten!

-
Sebastian Pizer schrieb:
Das mit den variadischen Funktionen funktioniert nur für PODs (plain old data structures). std::string ist kein POD. Dieses Sprachmerkmal ("...") ist sooo unattraktiv, dass ich gar nicht weiß, wie man es richtig benutzen müsste. :p
Da muss ich mich anschliessen. Mir gefällt aber audacias Code auch sonst nicht besonders...

Was spricht gegen eine Klasse, die den
operator<<anbietet und dessen Aufrufe an die Systemfunktionen weiterleitet?struct endl_t {} endl; // führt zum Weiterleiten ans System class streamer { public: template <typename T> streamer& operator<< (const T& value) { m_stream << value; return *this; } streamer& operator<< (const endl_t&) { system_output(m_stream.str()); m_stream.str(""); return *this; } private: void system_output(const std::string& message) { // tue je nach System etwas anderes } private: std::stringstream m_stream; };Anwendung:
streamer s; s << "Hallo" << 43 << endl;
-
Nexus schrieb:
Mir gefällt aber audacias Code auch sonst nicht besonders...

Wenn du etwas konkreter wärest, könnte man fast meinen, daß du auch einen Grund dafür hättest.
-
Muss man das noch begründen?
Ok, wie wär's mit:
- VarArgs sind typunsicher
- VarArgs funktionieren nur für PODs
- VarArgs erlauben dir hier nicht, beliebige benutzerdefinierte Typen T zu unterstützen, für die es einen operator<<(ostream&, const T&) gibt.
-
audacia schrieb:
Nexus schrieb:
Mir gefällt aber audacias Code auch sonst nicht besonders...

Wenn du etwas konkreter wärest, könnte man fast meinen, daß du auch einen Grund dafür hättest.
Der Code ist sicherlich gute für seinen bestimmten Anwendungszweck, lässt für den "modernen" Einsatz aber vermissen, eigene Objekte zu verwenden, bei denen z.B.
ostream& operator<<( ostream& o, const T& elem )überladen ist.Den Anwendungsfall des OP gibt es ja in mehreren Projekten, da ziehe ich eigentlich immer einen eigenen streambuf für cout/cerr vor, der bei einem flush (also auch endl) den Text überträgt/behandelt. Das eignet sich nur leider nicht für Internationalisierung à la gettext.
Internationalisierung hatte ich sowieso erst in einem einzigen Projekt (und dann noch in einem privaten), da hatte ich das etwa so gelöst:// Im Code: std::string error_text = makeError( "BlaNotFound", obj1, obj2, obj3, ... ); // In der Sprach-Datei (.txt-Datei): BlaNotFound: Object %1 could not be found in area %3 where %2=%4 ..."makeError" hat die Objekte dann per stringstream in Strings umgewandelt und in den Sprach-String eingefügt.
edit: Alter, über 5 Minuten für einen Post

-
Sebastian Pizer schrieb:
Muss man das noch begründen?
Daß "echte C++-Programmierer" variadische Funktionen als Teufelszeug empfinden, ist mir bekannt. Interessiert wäre ich eher an einer Erklärung des "auch sonst".
Sebastian Pizer schrieb:
- VarArgs sind typunsicher
Es ist aber problemlos möglich, die Funktionen z.B. im Debug-Build durch typsichere zu ersetzen.
Sebastian Pizer schrieb:
- VarArgs funktionieren nur für PODs
- VarArgs erlauben dir hier nicht, beliebige benutzerdefinierte Typen T zu unterstützen, für die es einen operator<<(ostream&, const T&) gibt.Das sind die klassischen Argumente für C++-Streams. Nur sind C++-Streams trotz aller Vorteile in der Theorie IMHO die schlechteste aller Alternativen; mit z.B. boost::format gibt es deutlich sinvollere.
Es hat schon einen Grund, daß keine Sprache außer C++ sich auf so etwas abstruses wie diese Streams einläßt. In allen vernünftigen Sprachen existiert printf() oder eine vergleichbare Alternative.
Badestrand schrieb:
Der Code ist sicherlich gute für seinen bestimmten Anwendungszweck
Eben. Darum ging es.
-
audacia schrieb:
Daß "echte C++-Programmierer" variadische Funktionen als Teufelszeug empfinden, ist mir bekannt. Interessiert wäre ich eher an einer Erklärung des "auch sonst".
Ich sagte extra "mir gefällt" und nicht etwa "audacias Code ist", es handelt sich also um eine subjektive Ansicht. Ich bin nicht unbedingt ein Fan von den C-Formatfunktionen und VarArgs. Aber auch das finde ich beispielsweise nicht allzu toll, da du hier davon ausgehst, dass der
std::stringan einem Stück sein muss, was nicht garantiert ist:&retval[0]Und
std::vsnprintfist nicht wirklich im Standard, sondern eher eine sicherere Version vonstd::vsprintf, oder?audacia schrieb:
Nur sind C++-Streams trotz aller Vorteile in der Theorie IMHO die schlechteste aller Alternativen; [...]
Es hat schon einen Grund, daß keine Sprache außer C++ sich auf so etwas abstruses wie diese Streams einläßt. In allen vernünftigen Sprachen existiert printf() oder eine vergleichbare Alternative.
Ja, so langsam wissen wir, dass dir die C++-Streams nicht gefallen. Du musst sie trotzdem nicht so darstellen, als ob man kaum mit ihnen arbeiten könnte, als ob sie die reinste Hölle wären. Sie sind vielleicht nicht der Höhepunkt der Ein-/Ausgabeprogrammierung, aber als so schlimm empfinde ich sie bei weitem nicht. Ich persönlich finde es sogar angenehmer, mit überladenem
operator<<zu arbeiten. Mir sind Typsicherheit und leichte Erweiterbarkeit für eigene Typen auch wichtig. Klar sehe ich auch ein, dass die Streams einige Nachteile haben, aber diese sind momentan nicht derart bedrückend, dass ich lieber auf dem C-Level arbeite. Das ist natürlich Geschmackssache und hängt von den jeweiligen Anforderungen ab...
-
Nexus schrieb:
Aber auch das finde ich beispielsweise nicht allzu toll, da du hier davon ausgehst, dass der
std::stringan einem Stück sein muss, was nicht garantiert istISO/IEC 14882 schrieb:
§21.3.4 basic_string element access
`const_reference operator[](size_type pos) const;reference operator[](size_type pos);
Returns: Ifpos < size(), returnsdata()[pos]. Otherwise, ifpos == size(), the const version returnscharT()` . Otherwise, the behavior is undefined.Da
basic_string::data()einen Zeiger zurückgibt, impliziert das, daß die Daten linear vorliegen. Es sei natürlich denn, ich übersehe etwas.Nexus schrieb:
Und
std::vsnprintfist nicht wirklich im Standard, sondern eher eine sicherere Version vonstd::vsprintf, oder?Soweit ich weiß, ist sie Teil von C99, und zwar ein Teil, den fast alle C++-Compiler implementieren.
Nexus schrieb:
Ja, so langsam wissen wir, dass dir die C++-Streams nicht gefallen. Du musst sie trotzdem nicht so darstellen, als ob man kaum mit ihnen arbeiten könnte, als ob sie die reinste Hölle wären.
Du beliebst maßlos zu übertreiben. Ich bemängele lediglich, daß sie stets als unumgänglich bessere Alternative dargestellt werden.
Nexus schrieb:
Das ist natürlich Geschmackssache und hängt von den jeweiligen Anforderungen ab...
So ist es. Die Anforderung war hier die einfache Portierung von C-Code.
-
audacia schrieb:
Daß "echte C++-Programmierer" variadische Funktionen als Teufelszeug empfinden, ist mir bekannt. Interessiert wäre ich eher an einer Erklärung des "auch sonst".
Ich bin doch ein "echter C++ Programmierer", zumindest schimpft man mich immer wieder als viel zu C++ treu, und ich könnte nicht ohne variadische Funktionen leben!
Wieso? Na deswegen:typedef char False; typedef struct { False dummy[2]; } True; template<typename FromT, typename ToT> struct IsConvertible { private: True test(ToT const&); False test(...); // <--------- FromT make(); public: static bool const RESULT = sizeof(True) == sizeof(test(make())); static bool const IS_SAME = false; }; template<typename SameT> struct IsConvertible<SameT, SameT> { static bool const RESULT = true; static bool const IS_SAME = true; };
Für etwas anderes, würde ich allerdings echt keine variadische Funktionen nehmen, also um solche Funktionen richtig aufzurufen. Ist mir viel zu unsicher. Ich habe sie ein paar Male verwenden wollen und immer ist mir irgendwo ein Fehler unterlaufen. Fängt bei kleinen Dingen an, wie zum Beispiel:
void fooFloats(std::size_t count, ...) { va_list list; va_start(list, count); for(std::size_t a = 0; a < count; ++a) { float f = va_arg(list, float); // mach was mit f. } va_end(list); } // ... fooFloats(5, 3.2, 234.0, 23, 23.0); // Alle Argumente sind falsch.Falsche Anzahl und (double, double, int, double), statt (float, float, float, float). Ein Fehler passiert einfach viel zu schnell. Aber wer mit dem Risiko leben kann ...
Da würde ich es eher über Proxies lösen, wie es auch Boost.Format gemacht hat.
Variadische Funktionen werde ich erst mit dem nächsten Standard anfangen zu verwenden, da man diese dann über Variadic Templates typsicher machen kann.audacia schrieb:
Da basic_string::data() einen Zeiger zurückgibt, impliziert das, daß die Daten linear vorliegen. Es sei natürlich denn, ich übersehe etwas.
Die Daten liegen zwar linear in diesem Pointer, aber wohin der Pointer zeigt, ist nicht definiert. Grundsätzlich darf hier ein neues Array angelegt werden, welches linear ist, während die interne Repräsentation nicht linear ist.
Dazu kannst du zum Beispiel dies hier lesen:
References, pointers, and iterators referring to the elements of a basic_string sequence may be invalidated by the following uses of that basic_string object:
— As an argument to non-member functions swap() (21.3.7.8), operator>>() (21.3.7.9), and getline() (21.3.7.9).
— As an argument to basic_string::swap().
— Calling data() and c_str() member functions.
— Calling non-const member functions, except operator[](), at(), begin(), rbegin(), end(), and rend().
— Subsequent to any of the above uses except the forms of insert() and erase() which return iterators, the first call to non-const member functions operator[](), at(), begin(), rbegin(), end(), or rend().Der zurückgegeben Pointer kann also invalidiert werden, wenn man erneut data aufruft. Also kann es sich um einen anderen Pointer handeln. Ich kenne zwar keine Standardbibliothek, welche dies wirklich so anwendet. Im nächsten Standard wird dies glaub ich sogar korrigiert.
Grüssli
-
Dravere schrieb:
audacia schrieb:
Da basic_string::data() einen Zeiger zurückgibt, impliziert das, daß die Daten linear vorliegen. Es sei natürlich denn, ich übersehe etwas.
Die Daten liegen zwar linear in diesem Pointer, aber wohin der Pointer zeigt, ist nicht definiert. Grundsätzlich darf hier ein neues Array angelegt werden, welches linear ist, während die interne Repräsentation nicht linear ist.
Du vergißt, daß ich den Wert der darunterliegenden Zeichens verändern darf. Eine Implementation, bei der meine obige Annahme nicht zutrifft, müßte also so aussehen:
template <typename CharT> class basic_string { private: CharT* actualData; CharT* consecutiveData; int lastRequestedChar; void applyCharModifications (void) { if (lastRequestedChar >= 0) { actualData[lastRequestedChar] = consecutiveData[lastRequestedChar]; lastRequestedChar = -1; } } public: const CharT* data (void) const { applyCharModifications (); delete consecutiveData; consecutiveData = new CharT[size () + 1]; memcpy (consecutiveData, actualData, sizeof (CharT) * (size() + 1)); return consecutiveData; } CharT& operator [] (unsigned idx) { applyCharModifications (); return const_cast <CharT*> (data ())[idx]; } ... };Dravere schrieb:
Dazu kannst du zum Beispiel dies hier lesen:
References, pointers, and iterators referring to the elements of a basic_string sequence may be invalidated by the following uses of that basic_string object:
— As an argument to non-member functions swap() (21.3.7.8), operator>>() (21.3.7.9), and getline() (21.3.7.9).
— As an argument to basic_string::swap().
— Calling data() and c_str() member functions.
— Calling non-const member functions, except operator[](), at(), begin(), rbegin(), end(), and rend().
— Subsequent to any of the above uses except the forms of insert() and erase() which return iterators, the first call to non-const member functions operator[](), at(), begin(), rbegin(), end(), or rend().Wenn ich das recht verstehe, sagt dieser Abschnitt ausdrücklich, daß folgendes zutrifft:
CharT& c1 = theString[0]; // first call to non-const member function operator [] () theString[1]; // second call - c1 bleibt gültig! CharT& c2 = theString[0]; // da c1 fürderhin gültig bleibt, muß &c1 == &c2 seinZwar scheint die von dir hervorgehobene Stelle dagegen zu sprechen, weil der Rückgabewert von
operator [] ()überdata()definiert ist, doch mir scheint die explizite Erwähnung des Indexoperators in der Auflistung von höherem Gewicht zu sein, und die postuliert nur beim erstmaligen Aufruf eine Invalidierung vorheriger Referenzen.Es ist richtig, daß die Bedingung
(&(operator [] (n)) + 1 == &(operator [] (n + 1))nicht erfüllt sein muß, aber die von dir zitierte Stelle macht selbst meinen obigen Versuch, diesen Fall herbeizuführen, nichtig. Um also unter Erfüllung der zusätzlichen Forderung meine Annahme zu widerlegen, müßtestd::basic_string<>für mit verschiedenen Indizes durchgeführteoperator [] ()-Aufrufe Zeiger auf unterschiedliche Strings zurückgeben, dabei aber beim Aufruf mit demselben Index jedesmal dieselbe Referenz, und sobald z.B.data()aufgerufen wird, sämtliche Änderungen wieder zum tatsächlichen String zusammenführen. Und das ist, da sind wir uns sicherlich einig, dermaßen abstrus, daß man es getrost außer acht lassen kann
Dravere schrieb:
Im nächsten Standard wird dies glaub ich sogar korrigiert.
Hoffentlich. Für
std::vector<>korrigierte das ja, wenn ich mich recht erinnere, bereits der 2003er-Standard.
-
@audacia,
Du verstehst mich wohl falsch oder ich dich. Ich wollte nur sagen, dass der zurückgelieferte Zeiger vondata, nicht auf das interne Array verweisen muss. Daher kannst du über die linearen Daten, welche sich an der Adresse des vondatazurückgelieferten Zeigers liegen, nicht darauf schliessen, dass sie intern auch linear angelegt sind. Es wäre möglich, dass sie imbasic_stringals Deque gespeichert sind.template<typename CharT, /* ... */ > class basic_string { std::deque<CharT> m_characters; // ... public: CharT const* data() const { static std::vector<CharT> buffer; buffer.resize(m_characters.size()); std::copy(m_characters.begin(), m_characters.end(), buffer.begin()); return &buffer[0]; } // ... CharT& operator [](std::size_t index) { return m_characters[index]; // entspricht durchaus data()[index] } // ... }; // Wenn du nun zum Beispiel sowas machst, dann hast du garantiertes undefiniertes Verhalten: std::string mystring = "test"; std::fill_n(&mystring[0], 4, 0); // Fehlannahme, dass der Zeiger auf ein zusammenhängendes Array verweist.Grüssli
-
Dravere schrieb:
@audacia,
Du verstehst mich wohl falsch oder ich dich.Das befürchte ich.
Dravere schrieb:
Ich wollte nur sagen, dass der zurückgelieferte Zeiger von
data, nicht auf das interne Array verweisen muss. Daher kannst du über die linearen Daten, welche sich an der Adresse des vondatazurückgelieferten Zeigers liegen, nicht darauf schliessen, dass sie intern auch linear angelegt sind. Es wäre möglich, dass sie imbasic_stringals Deque gespeichert sind.Das trifft zu, ist aber vollkommen irrelevant für meine obige Annahme.
Dravere schrieb:
// Wenn du nun zum Beispiel sowas machst, dann hast du garantiertes undefiniertes Verhalten: std::string mystring = "test"; std::fill_n(&mystring[0], 4, 0); // Fehlannahme, dass der Zeiger auf ein zusammenhängendes Array verweist.Nein, das ist keine Fehlannahme, denn dem Standard zufolge zeigt
data(), über das deroperator [] ()definiert ist, auf ein zusammenhängendes Array:ISO/IEC 14882 schrieb:
21.3.6 basic_stringstring operations
const charT* data() const;
3 Returns: If size() is nonzero, the member returns a pointer to the initial element of an array whose first
size() elements equal the corresponding elements of the string controlled by *this. If size() is
zero, the member returns a non-null pointer that is copyable and can have zero added to it.Was der Standard nicht vorschreibt, ist, daß ich mittels
(&operator [] (n))[1]das Zeichen unteroperator [] (n + 1)verändern kann. Aber hast du mal darüber nachgedacht, wie eine Implementation aussehen müßte, bei der das tatsächlich nicht der Fall ist? Falls nein, lies meinen obigen Post nocheinmal, da habe ich es ausgeführt.
-
audacia schrieb:
Du beliebst maßlos zu übertreiben. Ich bemängele lediglich, daß sie stets als unumgänglich bessere Alternative dargestellt werden.
Okay, das war etwas überspitzt formuliert. Mir ist jedoch aufgefallen, dass du die C++-Streams sehr oft schlecht machst - meiner Ansicht nach schlechter, als sie sind. Abgesehen davon ist die Aussage, Streams würden als unumgänglich bessere Alternative dargestellt, genauso übertrieben. Sie werden den Anfängern vielleicht als erste Möglichkeit vorgeschlagen, was auch seinen Grund hat. Etliche Fehlerquellen der C-Funktionen werden durch die Streams eingeschränkt oder ganz aus der Welt geschaffen, was einem Neuling sicher sehr entgegen kommt. Hingegen kann man als Anfänger die Nachteile der Streams eher leicht verkraften.
audacia schrieb:
Was der Standard nicht vorschreibt, ist, daß ich mittels
(&operator [] (n))[1]das Zeichen unteroperator [] (n + 1)verändern kann. Aber hast du mal darüber nachgedacht, wie eine Implementation aussehen müßte, bei der das tatsächlich nicht der Fall ist? Falls nein, lies meinen obigen Post nocheinmal, da habe ich es ausgeführt.Klar werden es vernünftige Implementierungen nicht so lösen - zum Beispiel würden diese für
c_str()auch nicht ein neues Array mit '\0' erstellen.Scheint schon so, als wäre diese Annahme in den meisten Fällen gewährleistet, darauf verlassen würde ich mich trotzdem nicht. Zum Beispiel muss
data()auch keinen nullterminierten String zurückgeben. Oder kann Nullen innerhalb des Strings zurückgeben, die von den C-Funktionen als Terminierung angesehen werden.
-
audacia schrieb:
Nein, das ist keine Fehlannahme, denn dem Standard zufolge zeigt
data(), über das deroperator [] ()definiert ist, auf ein zusammenhängendes Array:audacia schrieb:
Was der Standard nicht vorschreibt, ist, daß ich mittels
(&operator [] (n))[1]das Zeichen unteroperator [] (n + 1)verändern kann.Die beiden Sätze sind meiner Meinung nach der komplette Widerspruch.
operator [](index)ist nur überdata()[index]definiert, also dass hier der gleiche Wert zurückkommt. Es ist nicht definiert, dass der Zeiger auf das zurückgegebene Element gleich sein muss. Daher ist dies undefiniertes Verhalten:std::fill_n(&mystring[0], 4, 0);Weil man hier eben davon ausgeht, dass man mittels
(&operator [] (n))[1]das Zeichen unteroperator [] (n + 1)verändern kann. Was du selber sagst, dass es nicht vom Standard vorgeschrieben ist ...audacia schrieb:
Aber hast du mal darüber nachgedacht, wie eine Implementation aussehen müßte, bei der das tatsächlich nicht der Fall ist?
Ja, ich bin sogar schon mal soweit gegangen, dass ich dies selber getestet habe. Es gibt durchaus eine gewisse Berechtigung für sowas. Wenn man nämlich Einfügeoperationen im
std::stringbeschleunigen möchte, also nicht nur das Anfügen am Ende, dann kann man hier durch eine veränderte Speicherverwaltung eine gewisse Optimierung erzielen. Man kann zum Beispiel auch davon ausgehen, dassc_strunddataMethoden sind, welche eher seltener aufgerufen werden und daher ruhig etwas teuerer sein dürfen.
Also ganz so abwegig finde ich eine solche Implementation nicht.Aber es ist wohl wie bei der Verwendung von variadischen Funktionen, wenn man das Risiko eingehen möchte, wobei man hier sogar nicht standardkonforme Operationen durchführt ... Du bist wohl einfach risikofreudiger als andere

Grüssli
-
Dravere schrieb:
Die beiden Sätze sind meiner Meinung nach der komplette Widerspruch.
operator [](index)ist nur überdata()[index]definiert, also dass hier der gleiche Wert zurückkommt.operator [] ()gibt eine Referenz zurück, über die du den Wert ändern kannst. Das macht den Unterschied aus.Dravere schrieb:
audacia schrieb:
Aber hast du mal darüber nachgedacht, wie eine Implementation aussehen müßte, bei der das tatsächlich nicht der Fall ist?
Ja, ich bin sogar schon mal soweit gegangen, dass ich dies selber getestet habe. Es gibt durchaus eine gewisse Berechtigung für sowas. Wenn man nämlich Einfügeoperationen im
std::stringbeschleunigen möchte, also nicht nur das Anfügen am Ende, dann kann man hier durch eine veränderte Speicherverwaltung eine gewisse Optimierung erzielen. Man kann zum Beispiel auch davon ausgehen, dassc_strunddataMethoden sind, welche eher seltener aufgerufen werden und daher ruhig etwas teuerer sein dürfen.
Also ganz so abwegig finde ich eine solche Implementation nicht.Na, dann zeig mal her. Davon ausgehend, daß die Motivation für deine Implementation "nicht ganz abwegig" ist, behaupte ich: entweder ist deine Implementation nicht standardkonform, oder meine obige Annahme trifft zu.
Dravere schrieb:
Aber es ist wohl wie bei der Verwendung von variadischen Funktionen, wenn man das Risiko eingehen möchte
Keine Sorge, bei mir sind
printf()und dergleichen allesamt typsicher
-
audacia schrieb:
operator [] ()gibt eine Referenz zurück, über die du den Wert ändern kannst. Das macht den Unterschied aus.Aber nur der Wert an dieser Stelle. Du kannst nicht vom referenzierten Wert den Zeiger holen, diesen inkrementieren und dann die Stelle beschreiben oder lesen. Das ist undefiniertes Verhalten. Aber genau das macht der präsentierte Code von dir oder der von mir mit
std::fill_n.audacia schrieb:
Na, dann zeig mal her. Davon ausgehend, daß die Motivation für deine Implementation "nicht ganz abwegig" ist, behaupte ich: entweder ist deine Implementation nicht standardkonform, oder meine obige Annahme trifft zu.
Würde ich gerne tun, hätte ich bereits oben schon getan, wenn ich denn könnte. Es ist schon ein wenig her, als ich das gemacht habe, und in der Zwischenzeit gab es einen Zwischenfall mit einem defekten S-ATA Kabel, welcher leider unteranderem diesen Code zerstört hat.

Da ich es nur zu Testzwecken erstellt hatte und es nicht für wichtig hielt, gibt es davon auch kene Backups und habe bis heute keine Lust empfunden, dies nochmals zu testen, bzw. zu implementieren. Auch jetzt nicht

Grüssli
-
audacia schrieb:
Keine Sorge, bei mir sind
printf()und dergleichen allesamt typsicherWelche Typsicherheit meinst Du denn? Die Sorte von Typsicherheit, die Typfehler schon zur Kompilierzeit abfangen kann oder nur die Sorte, die Laufzeitüberprüfungen einbaut?
Gruß,
SP
-
so was in der art?!
#include <algorithm> #include <deque> #include <memory> #include <string> #include <vector> template < typename TChar, typename TTraits = std::char_traits <TChar>, typename TAlloc = std::allocator<TChar> > struct my_string { public: typedef TTraits traits_type; typedef typename traits_type::char_type value_type; typedef TAlloc allocator_type; typedef typename allocator_type::size_type size_type; typedef typename allocator_type::const_reference const_reference; typedef typename allocator_type::reference reference; typedef typename allocator_type::const_pointer const_pointer; typedef typename allocator_type::pointer pointer; private: typedef std::deque<value_type, allocator_type> saving_container; typedef std::vector<value_type, allocator_type> c_string_container; saving_container string_data; public: typedef typename saving_container::const_iterator const_iterator; typedef typename saving_container::iterator iterator; public: my_string(const_pointer _string) : string_data( _string, _string+traits_type::length(_string) ) {} const_iterator begin() const { return string_data.begin(); } iterator begin() { return string_data.begin(); } const_iterator end() const { return string_data.end(); } iterator end() { return string_data.end(); } const_pointer c_str() const { return data(); } const_pointer data() const { static c_string_container c_array; c_array.resize(size()+1); std::copy(string_data.begin(), string_data.end(), c_array.begin()); return &*c_array.begin(); } const_reference operator []( size_type pos ) const { return string_data[pos]; } reference operator []( size_type pos ) { return string_data[pos]; } size_type size() const { return string_data.size(); } my_string& operator+= (const my_string &rhs) { string_data.insert( string_data.end(), rhs.string_data.begin(), rhs.string_data.end() ); return *this; } }; typedef my_string<char> string; #include <algorithm> #include <iostream> int main() { string a = "asdasdasdasdasdasdasdadsasdasd"; a += "qweqweqweqweqweqweqweqweqweqweqweqweqwe"; a += "yxcyxcyxcyxcyxcyxcyxcyxcyxcyxcyxcyxcyxc"; a[a.size()-1] = 'f'; std::cout << a.c_str() << std::endl; // std::fill_n(&a[0], a.size(), 'A'); std::cout << a.c_str() << std::endl; system("PAUSE"); }ich nehm mal an, dass ich mich an alles gehalten habe, was im standard steht ^^
vollständig ist es natürlich nicht - aber ich denke, es verdeutlicht das, was dravere geschrieben hat...bb