(boost::) indexable



  • Hi ^^

    Ich habe ein Problem mit indexable (aus boost rauskopiert, weil ich es abgeben muss und keine Lust habe, 20 boost-Header mitzuliefern):

    #include <iostream>
    
    template <typename T, typename const_reference, typename size_type>
    struct indexable
    {
    	const_reference operator[](size_type n) const
    	{
    		return *(static_cast<const T&>(*this) + n);
    	}
    };
    
    struct IPv4 : indexable <IPv4, const unsigned char&, size_t>
    {
    public:
    	typedef unsigned char value_type;
    private:
    	typedef unsigned int saving_type;
    	static const char Seperator = '.';
    	value_type vals[4];
    public:
    	explicit IPv4(value_type a = value_type(), value_type b = value_type(), value_type c = value_type(), value_type d = value_type())
    	{
    		vals[0] = a;
    		vals[1] = b;
    		vals[2] = c;
    		vals[3] = d;
    	}
    
    	value_type& operator[] (size_t i)
    	{
    		return vals[i];
    	}
    
    	friend std::ostream& operator << (std::ostream& s, const IPv4 &these)
    	{
    		for (size_t i(0), e(4-1); i != e; ++i)
    		{
    			s << static_cast <IPv4::saving_type> (these[i])
    			  << IPv4::Seperator;
    		}
    		s << static_cast <IPv4::saving_type> (these[3]);
    		return s;
    	}
    };
    
    int main()
    {
    	IPv4 eins (127, 0, 0, 1);
    	std::cout << eins;
    }
    

    Der Compiler (MSVC) sagt mir jetzt aber:

    MSVC schrieb:

    1>...\test\main.cpp(38) : error C2678: binary '[' : no operator found which takes a left-hand operand of type 'const IPv4' (or there is no acceptable conversion)
    1> ...\test\main.cpp(29): could be 'IPv4::value_type &IPv4::operator [](size_t)'
    1> while trying to match the argument list '(const IPv4, size_t)'
    1>...\test\main.cpp(41) : error C2678: binary '[' : no operator found which takes a left-hand operand of type 'const IPv4' (or there is no acceptable conversion)
    1> ...\test\main.cpp(29): could be 'IPv4::value_type &IPv4::operator [](size_t)'
    1> while trying to match the argument list '(const IPv4, int)'

    Ja - das Problem sollte eigtl offensichtlich sein und beim Code sollte es auch nichts zu kommentieren geben...

    Hat jemand ne Idee?

    bb



  • return *(static_cast<const T&>(*this) + n);
    

    sicher das das so klappt? du erhöhst ja die refernez um n und indizierst das wieder
    versuchs mal so

    return *(reinterpret_cast<const T *>(this) + n);
    

  • Administrator

    Was um alles in der Welt hast du da vor? Hast du mal die Dokumentation dazu gelesen? boost::indexable ist für Iteratoren gedacht, also T soll eine Iteratoren Klasse sein. Diese Iterator Klasse muss den operator+ überladen haben.

    Da lies selber nach:
    http://www.boost.org/doc/libs/1_39_0/libs/utility/operators.htm#dereference

    Oder vielleicht wäre dieser Artikel noch interessant:
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-240706.html

    Jedenfalls verwendest du boost::indexable definitiv falsch.

    Grüssli



  • hilfehilfe schrieb:

    sicher das das so klappt? du erhöhst ja die refernez um n und indizierst das wieder

    Die Umsetzung ist korrekt, die Verwendung nicht.

    @unskilled:
    um indexable<T, R, D> zu verwenden, muss operator+ (T, D) und T::operator*() definiert sein, und zwar so dass die Addition konvertierbar in T ist und die Dereferenzierung etwas konvertierbares in R liefert. Schau dir am Besten die Beispiele in der Doku nochmal an.



  • verdammt - hätt ich auch selbst drauf kommen können xD
    ty ^^

    @dravere: den artikel hab ich scho gelesen - allerdings ist indexable da nicht gezeigt worden - und den einen satz, wo es tatsächlich steht, hab ich iwie net gelesen -.-

    allerdings hab ich in der boost-doku kein bsp gefunden - vll bin ich ja auch einfach nur blind, aber eigtl hab ich doch nen ganzes weilchen gesucht ^^

    bb

    edit: eigtl such ich was, was mir die arbeit abnimmt, op[] immer einmal für const und einmal für non-const zu überladen... aber irgendwie funtzt das nich - geht das überhaupt, oder geht das bei op[] gerade mal nicht so schön, wie bei dem rest?
    ich dachte, ich überlade den non-const und der für const macht eben nen const cast und ruf dann meinen op[] non-const auf, also so was hier in der richtung:

    template <typename T, typename const_reference, typename size_type>
    struct indexable
    {
    	const_reference operator[](size_type n) const
    	{
    		return (static_cast<const T*>(this)->operator[](n));
    	}
    };
    

    aber als es so am anfang net geklappt hatte, hab ich dann einfach das aus boost 1:1 übernommen, ohne noch ma nachzudenken -.-



  • für das was du versuchst zu tun ist indexable nicht gedacht. Es ist nur für iteratorartige Objekte gedacht, nicht für containerartige.
    Für die ewig gleichen const/non-const-implementierungen von Containerzugriffen weiß ich auch keinen gescheiten Rat. Wenn du was findest was die Arbeit abnimmt sag Bescheid, ich bin grad dabei ne std::vector-Implementierung zu schreiben, da gibts auch einen ganzen Stapel doppelte Accessor 😉

    PS: aber danke für den Irrtum, die Iterator-Operatoren werden im nächsten Artikel zu boost::operators besprochen, da kann ich gleich nochmal drauf hinweisen dass das nicht für Container da ist 😉



  • pumuckl schrieb:

    PS: aber danke für den Irrtum, die Iterator-Operatoren werden im nächsten Artikel zu boost::operators besprochen, da kann ich gleich nochmal drauf hinweisen dass das nicht für Container da ist 😉

    Nur deshalb hab ich das doch gepostet 😉


  • Administrator

    @unskilled,
    Ist dein Problem denn nun gelöst? Also du wolltest ja wohl sowas machen, oder?

    #define MACRO_CREATE_OPERATOR_INDEXABLE(C, R, S) \
    	R operator [](S index) \
    	{ \
    		return const_cast<R>((*static_cast<C const*>(this))[index]); \
    	}
    
    class Foo
    {
    	unsigned char data[4];
    
    public:
    	MACRO_CREATE_OPERATOR_INDEXABLE(Foo, unsigned char&, std::size_t)
    
    	unsigned char const& operator [](std::size_t index) const
    	{
    		return data[index];
    	}
    };
    

    Ja, Makros sind nicht so schön, aber es ist am einfachsten, so dass auch die Überladung funktioniert. Man bemerke übrigens, dass ich aus der nicht konstanten Version auf die konstante Version zugreife, ist etwas sicherer 😉

    Grüssli



  • und was bitte macht den Unterschied zwischen den Makro und einem einfach als Member definierten Operator? - Mal abgesehn davon dass das Makro eine Zeile mehr bei der Definition und eine zusätzliche Zeile für den Aufruf braucht (und am besten noch eine Zeile für den #undef) 😉



  • pumuckl schrieb:

    und was bitte macht den Unterschied zwischen den Makro und einem einfach als Member definierten Operator? - Mal abgesehn davon dass das Makro eine Zeile mehr bei der Definition und eine zusätzliche Zeile für den Aufruf braucht (und am besten noch eine Zeile für den #undef) 😉

    naja - es geht halt für jeden container - aber so richtig gefallen tuts mir auch nich -.-
    danke trotzdem, mal gucken, ob ichs nehm oder doch den gleichen code zweimal schreibe ^^

    bb


  • Administrator

    pumuckl schrieb:

    und was bitte macht den Unterschied zwischen den Makro und einem einfach als Member definierten Operator? - Mal abgesehn davon dass das Makro eine Zeile mehr bei der Definition und eine zusätzliche Zeile für den Aufruf braucht (und am besten noch eine Zeile für den #undef) 😉

    Du kannst es wiederverwenden? Das Makro ist natürlich nicht für eine einzige Klasse gedacht, was allerdings das indexable wohl auch nicht gedacht ist 😉

    Wenn man die Sache als Klasse implementiert, hat man leider das Problem, dass die Überladung nicht hinhaut, weil es ein anderer Scope ist. Da müsste man gleich auch noch ein using hinschreiben, was die Sache gleich komplex macht. Deshalb das Makro statt einer Klasse, von welcher man ableiten kann.

    Grüssli


Anmelden zum Antworten