Code für Ausgabe vereinfachen.



  • 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



  • Dravere schrieb:

    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 .

    Das ist mir schon klar. Aber der Rückgabewert des Indexoperators muß praktisch *const_cast <CharT*> (data () + n) sein - wie von der Definition des Index-Operators gefordert -, und data() gibt einen Zeiger auf den Datenbestand des Strings in Form eines Arrays zurück. Der Lesezugriff auf const_cast <CharT*> (data () + n)[1] ist daher legal; der Schreibzugriff zwar nicht, aber in der Praxis existiert keine Prozessorarchitektur, die eine Page-Granularität einzelner Bytes hat, so daß der Schreibzugriff auch für const_cast <CharT*> (data () + n)[1] funktionieren wird. Jedoch nicht garantiert ist, daß eine Änderung auf diesem Wege auch den tatsächlichen String ändert - aber wie ich oben schrieb, wäre eine Implementation, die das ausnutzt, am Rande des Wahnsinns.



  • Sebastian Pizer schrieb:

    Welche Typsicherheit meinst Du denn? Die Sorte von Typsicherheit, die Typfehler schon zur Kompilierzeit abfangen kann oder nur die Sorte, die Laufzeitüberprüfungen einbaut?

    Laufzeitüberprüfung. Die Überprüfung zur Übersetzungszeit ist nicht möglich, wenn der Format-String erst zur Laufzeit bestimmt wird (z.B. mit gettext). Für die Fälle, wo es aber möglich ist, gibt es z.B. -Wformat bei GCC.

    unskilled schrieb:

    ich nehm mal an, dass ich mich an alles gehalten habe, was im standard steht ^^

    Hast du nicht - dein Indexoperator gibt nicht data()[n] zurück, wie vom Standard gefordert.



  • Aber der Rückgabewert des Indexoperators muß praktisch *const_cast <CharT*> (data () + n) sein

    wieso sollte er?

    standard, 21.3.4 basic_string element access schrieb:

    Returns: If pos < size(), returns *(begin() + pos ). Otherwise, if pos == size(), the const version returns charT(). Otherwise, the behavior is undefined.

    das heißt, mein bsp. is doch nich ganz standard-konform, weil ich bei pos==size keine charT() garantieren kann, aber dann muss ich eben doch die 0 mitspeichern und es ist wieder standardkonform

    bb

    edits: falls es doch iwo steht, dann such ma pls die stelle raus, wo das steht...
    das war der working draft, wo das so drin steht ^^
    hab ma noch nen älteren standard (wieder nur nen draft-.-) gefunden(eher uralt, weil von 1996 - aber nen besseren hab ich nich gefunden)
    da steht das drin:

    1 Effects: The reference returned is invalid after any subsequent call to c_str(), data(), or any nonconst member function for the object.
    2 Returns: If pos < size(), returns data()[pos]. Otherwise, if pos == size(), the const version returns traits::eos(). Otherwise, the behavior is undefined.

    wenn das noch gültig ist, dann hast du natürlich recht ^^ aber dann kann ich einfach sagen, dass ich nach dem kommenden standard standardkonform wäre 😉



  • unskilled schrieb:

    edits: falls es doch iwo steht, dann such ma pls die stelle raus, wo das steht...
    das war der working draft, wo das so drin steht ^^
    hab ma noch nen älteren standard (wieder nur nen draft-.-) gefunden(eher uralt, weil von 1996 - aber nen besseren hab ich nich gefunden)

    Hättest du den Thread gelesen, so wäre dir vielleicht aufgefallen, daß ich die entsprechende Stelle oben bereits zitiert habe, und zwar aus dem 2003er-Standard.

    audacia schrieb:

    ISO/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.



  • stimmt 😉

    hab ich ma wieder nur die hälfte gelesen...
    dann fällt mir jetzt gerade auch keine möglichkeit ein - wirst du wohl recht haben ^^
    Imho ist returns data()[pos] eigtl schon die Garantie dafür, dass intern ein vector genommen wird...
    aber in nem Jahr ist das dann hoffentlich nicht mehr so ^^

    bb


  • Administrator

    audacia schrieb:

    Das ist mir schon klar. Aber der Rückgabewert des Indexoperators muß praktisch *const_cast <CharT*> (data () + n) sein - wie von der Definition des Index-Operators gefordert -, ...

    Nein, diese Forderung wird vom Standard nicht gestellt. Es muss der gleiche Wert sein, aber nicht das gleiche Objekt. Du überinterpretierst die Aussage des Standards. Es kann ja auch nicht das gleiche Objekt sein, denn sonst würde diese Definition gegen die const correctness verstossen.

    Was hier gefordert wird ist eigentlich das folgende:

    std::string mystring = "hello";
    
    assert(mystring.data()[2] == mystring[2]);
    

    Aber ganz sicher nicht dies:

    assert(&mystring.data()[2] == &mystring[2]); // -> ist undefiniert
    

    Grüssli



  • Dravere schrieb:

    audacia schrieb:

    Das ist mir schon klar. Aber der Rückgabewert des Indexoperators muß praktisch *const_cast <CharT*> (data () + n) sein - wie von der Definition des Index-Operators gefordert -, ...

    Nein, diese Forderung wird vom Standard nicht gestellt. Es muss der gleiche Wert sein, aber nicht das gleiche Objekt. Du überinterpretierst die Aussage des Standards. Es kann ja auch nicht das gleiche Objekt sein, denn sonst würde diese Definition gegen die const correctness verstossen.

    Du unterintepretierst die Forderung. Daß der Standard den Rückgabewert des Indexoperators - eine non-const-Referenz! - als data()[n] definiert, ist an sich bereits ungenau. Meine Interpretation ist: der const_cast<> wurde zugunsten der Klarheit weggelassen; stünde er da, wäre es eindeutig. Deine Interpretation aber zieht sich auf Wertgleichheit zurück, was beim Umgang mit Referenzen IMHO weit hergeholt ist.

    Ein interessanter Aspekt dabei ist die Tatsache, daß die Definition in den Entwürfen des 1998er-Standards und möglicherweise auch im finalen Standard nicht auf data() zurückgriff, sondern sich der Iteratoren bediente. Das wird sicherlich nicht ohne Grund geändert worden sein.


  • Administrator

    Und es ist mal wieder soweit, es ist eine Frage der Interpretation. Wieso können die Leute vom Standard eigentlich die Dinge nicht explizit angeben? Scheiss egal, wenn sie sich wiederholen, dafür aber so, dass es klar und verständlich ist. Bei std::vector steht ja auch:

    The elements of a vector are stored contiguously, meaning that if v is a vector<T, Allocator> where T is some type other than bool, then it obeys the identity &v[n] == &v[0] + n for all 0 <= n < v.size().

    Bei std::basic_string steht dies nirgends, deshalb gehe ich davon aus, dass dies nicht der Fall sein muss und bei der Definition über data()[n] geht es nur um Wertgleichheit. Wenn sie wirklich das const_cast weggelassen haben, dann ist es ein Fehler im Standard. Wer lässt bitte Genauigkeit im Standard weg?

    Grüssli



  • audacia schrieb:

    Sebastian Pizer schrieb:

    Welche Typsicherheit meinst Du denn? Die Sorte von Typsicherheit, die Typfehler schon zur Kompilierzeit abfangen kann oder nur die Sorte, die Laufzeitüberprüfungen einbaut?

    Laufzeitüberprüfung. Die Überprüfung zur Übersetzungszeit ist nicht möglich, wenn der Format-String erst zur Laufzeit bestimmt wird (z.B. mit gettext). Für die Fälle, wo es aber möglich ist, gibt es z.B. -Wformat bei GCC.

    Funktioniert das auch für Deine selbst geschriebene var-args Funktion? :p

    Gruß,
    SP



  • Sebastian Pizer schrieb:

    audacia schrieb:

    Sebastian Pizer schrieb:

    Welche Typsicherheit meinst Du denn? Die Sorte von Typsicherheit, die Typfehler schon zur Kompilierzeit abfangen kann oder nur die Sorte, die Laufzeitüberprüfungen einbaut?

    Laufzeitüberprüfung. Die Überprüfung zur Übersetzungszeit ist nicht möglich, wenn der Format-String erst zur Laufzeit bestimmt wird (z.B. mit gettext). Für die Fälle, wo es aber möglich ist, gibt es z.B. -Wformat bei GCC.

    Funktioniert das auch für Deine selbst geschriebene var-args Funktion? :p

    Ja. Erfordert drei oder vier zusätzliche Zeilen neben der Deklaration.

    Dravere schrieb:

    Wieso können die Leute vom Standard eigentlich die Dinge nicht explizit angeben?

    Weil die menschliche Sprache immer Interpretationsfreiheit läßt. In dem konkreten Fall stimme ich zu, das hätte man sehr einfach deutlicher ausdrücken können, aber solange keine formale Grammatik existiert, um die Semantik einer Sprache zu beschreiben - mir ist keine bekannt -, werden derartige Ungenauigkeiten unvermeidlich auftauchen.


Anmelden zum Antworten