Strikte Typdefinition



  • Hallo,

    mir ist folgendes aufgefallen:

    typedef int my_int;

    Ich kann my_int überall dort übergeben, wo ein int erwartet wird. Gibt es eine Möglichkeit dass zu unterbinden? Ich würde gerne,dass ich nicht um einen cast herumkomme.

    Danke euch! 🙂

    Bye, donnerCobra


  • Administrator

    typedef erstellt nur ein Synonym. Der Typ bleibt gleich. Wenn du einen neuen Typ willst, dann kommst du um class oder struct nicht herum.

    Grüssli



  • Dafür nutze ich immer eine Wrapper-Klasse:

    template<typename T> class wrapper
    {
    public:
    	explicit wrapper(T t)
    		: _t(t)
    	{}
    
    	operator T() const { return _t; }
    
    private:
    	T _t;
    };
    

    Benutzung dann so:

    typedef wrapper<int> my_int;
    
    my_int x = my_int(42);
    // 
    int y = x;
    

    Wenn du letzteres unterbinden willst, dann einfach den Operator entfernen und dafür dann z.B. eine Methode "cast()" anbieten.

    Wenn du jedoch Zuweisungen der Art

    typedef int my_int;
    typedef int my_int2;
    
    my_int x;
    my_int2 y;
    
    x = y; // <-- dies unterbinden?
    

    verbieten willst (d.h. Compiler-Fehler), dann kann ich dir eine andere Klasse anbieten, die nur im TEST-Modus Fehler wirft (wenn du obige Wrapper-Klasse nicht verwenden willst).



  • Super, vielen Dank!

    Zur Wrapperklasse: ist dies aufgrund des Wrappers (ok, ein bisschen Overhead) langsamer als der reine Datentyp?

    @ Th69 Wenn du mir die eine Klasse zur Verfügung stellen könntest, wäre das super.

    Bye, donnerCobra



  • Nein, das sollte im Release-Mode eigentlich alles inline ausgeführt werden (ist also nur syntaktischer Overhead).

    Und hier noch meine Header-Datei 'type.h' mit der Klasse 'type':

    #ifndef TYPE_H
    #define TYPE_H
    
    #ifdef TEST_TYPE
    
    template<typename T, class NAME> class type
    {
    public:
    	typedef type<T, NAME> THIS;
    
    	type() {}
    	type(const THIS& t) { x = t.x; }
    	type(const T& _t) { x = _t; }
    
    //	operator T() const { return x; }
    	T value() const { return x; }
    	template<class U> U cast() const { return (U)x; }
    
    	THIS& operator  = (const THIS& t) { x  = t.x; return *this; }
    	THIS& operator *= (const THIS& t) { x *= t.x; return *this; }
    	THIS& operator /= (const THIS& t) { x /= t.x; return *this; }
    	THIS& operator %= (const THIS& t) { x %= t.x; return *this; }
    	THIS& operator += (const THIS& t) { x += t.x; return *this; }
    	THIS& operator -= (const THIS& t) { x -= t.x; return *this; }
    	THIS& operator <<=(const THIS& t) { x <<= t.x; return *this; }
    	THIS& operator >>=(const THIS& t) { x >>= t.x; return *this; }
    	THIS& operator &= (const THIS& t) { x &= t.x; return *this; }
    	THIS& operator |= (const THIS& t) { x |= t.x; return *this; }
    	THIS& operator ^= (const THIS& t) { x ~= t.x; return *this; }
    
    	const THIS& operator ++ () { ++x; return *this; }
    	const THIS& operator -- () { --x; return *this }
    	THIS operator ++ (int) { THIS t = *this; ++x; return t; }
    	THIS operator -- (int) { THIS t = *this; --x; return t; }
    
    	bool operator ==(const THIS& t) const { return x == t.x; }
    	bool operator !=(const THIS& t) const { return x != t.x; }
    	bool operator < (const THIS& t) const { return x <  t.x; }
    	bool operator <=(const THIS& t) const { return x <= t.x; }
    	bool operator > (const THIS& t) const { return x >  t.x; }
    	bool operator >=(const THIS& t) const { return x >= t.x; }
    
    	THIS operator * (const THIS& t) const { return THIS(x * t.x); }
    	THIS operator / (const THIS& t) const { return THIS(x / t.x); }
    	THIS operator % (const THIS& t) const { return THIS(x % t.x); }
    	THIS operator + (const THIS& t) const { return THIS(x + t.x); }
    	THIS operator - (const THIS& t) const { return THIS(x - t.x); }
    	THIS operator <<(const THIS& t) const { return THIS(x << t.x); }
    	THIS operator >>(const THIS& t) const { return THIS(x >> t.x); }
    	THIS operator & (const THIS& t) const { return THIS(x & t.x); }
    	THIS operator | (const THIS& t) const { return THIS(x | t.x); }
    	THIS operator ^ (const THIS& t) const { return THIS(x ^ t.x); }
    	THIS operator + () const { return THIS( x); }
    	THIS operator - () const { return THIS(-x); }
    	THIS operator ! () const { return THIS(!x); }
    	THIS operator ~ () const { return THIS(~x); }
    
    	friend bool operator ==(const T& _t, const THIS& t) { return _t == t.x; }
    	friend bool operator !=(const T& _t, const THIS& t) { return _t != t.x; }
    	friend bool operator < (const T& _t, const THIS& t) { return _t <  t.x; }
    	friend bool operator <=(const T& _t, const THIS& t) { return _t <= t.x; }
    	friend bool operator > (const T& _t, const THIS& t) { return _t >  t.x; }
    	friend bool operator >=(const T& _t, const THIS& t) { return _t >= t.x; }
    
    	friend THIS operator * (const T& _t, const THIS& t) { return THIS(_t * t.x); }
    	friend THIS operator / (const T& _t, const THIS& t) { return THIS(_t / t.x); }
    	friend THIS operator % (const T& _t, const THIS& t) { return THIS(_t % t.x); }
    	friend THIS operator + (const T& _t, const THIS& t) { return THIS(_t + t.x); }
    	friend THIS operator - (const T& _t, const THIS& t) { return THIS(_t - t.x); }
    	friend THIS operator <<(const T& _t, const THIS& t) { return THIS(_t << t.x); }
    	friend THIS operator >>(const T& _t, const THIS& t) { return THIS(_t >> t.x); }
    	friend THIS operator & (const T& _t, const THIS& t) { return THIS(_t & t.x); }
    	friend THIS operator | (const T& _t, const THIS& t) { return THIS(_t | t.x); }
    	friend THIS operator ^ (const T& _t, const THIS& t) { return THIS(_t ^ t.x); }
    
    private:
    	T x;
    };
    
    #define TYPEDEF(T, NAME)\
    	struct _S_##NAME\
    	{\
    		static const char * const name() { return #NAME; }\
    	};\
    	typedef type<T, _S_##NAME> NAME;\
    	/* partial template */\
    	template<class U> U CAST(const NAME& t)\
    	{\
    		return U(t.value()); /* t.template cast<U>(); */\
    	}
    
    #else
    
    #define TYPEDEF(T, NAME)\
    	typedef T NAME;
    
    template<class U, class T> U CAST(const T& t)
    {
    	return U(t);
    };
    
    #endif
    
    #endif
    

    Damit kannst du verschiedene typedefs voneinander trennen, obwohl sie auf demselben Datentyp beruhen.
    Hier meine Testfunktion dazu:

    #define TEST_TYPE
    #include "type.h"
    
    TYPEDEF(int, ID);
    TYPEDEF(int, INDEX);
    
    static INDEX st_idx[] = { 0, 10, 5, 3, 2, 1 };
    
    void test_type()
    {
    	ID id = 42;
    	INDEX index = 101;
    	INDEX index2 = st_idx[0];
    
    	id *= 4;
    	id = 10 - id;
    	ID id2 = id++;
    	id = -id2;
    	id2 -= id;
    	id = 2 * (id2 + 5);
    
    	if(id2 != 0)
    		id2 = -1;
    
    	if(0 > id2)
    		id2 = id - 100;
    
    	if(id == id2)
    		++id2;
    
    	ID *pId = &id;
    	*pId = 5;
    
    	id2 = *pId;
    
    //  errors! --> good!
    /*	id = index;
    	id *= index;
    	ID id3 = index;
    	ID id4(index);
    	if(id == index)
    		index = 5 + id;
    */
    	// with explicit casting --> OK
    	if(CAST<INDEX>(id) == index)
    		id += CAST<ID>(index);
    
    	int i = CAST<int>(id);
    	i = i;
    	unsigned short us = CAST<unsigned short>(id);
    	us = us;
    }
    

    Du brauchst dann einfach z.B. im Debug-Modus das '#define TEST_TYPE' aktivieren - und im Release-Mode dann normal kompilieren (um nicht den unnötigen Overhead der internen Klasseninstanzen für das template 'class <...> type' zu haben.
    Du mußt dazu dann allerdings statt

    typedef int my_int;
    

    dann immer das define

    TYPEDEF(int, my_int);
    

    benutzen.



  • selbst mittels präprozessor könnte man das hübscher machen... die richtige wahl wäre hier aber(wie der name des makros schon sagt :P) ein einfaches typedef...
    den name halte ich für sinnlos...

    /* wrapper.h */
    namespace detail
    {
      template<typename T>
      struct foo
      {
        /*...*/
      };
    }
    
    #if defined(NDEBUG)
      template<typename T>
      struct wrapper
      {
        typedef detail::foo<T> type;
      };
    #else
      template<typename T>
      struct wrapper
      {
        typedef T type;
      };
    #endif
    

    oder aber

    /* wrapper.h */
    template<typename T>
    struct wrapper
    {
      /*...*/
    
    # if defined(NDEBUG)
        typedef wrapper type;
    # else
        typedef T type;
    # endif
    };
    

    in beiden fällen würde das so einfach und intuitiv zu nutzen gehen:

    /* main.cpp */
    
    typedef wrapper<int>::type id_t;
    
    int main()
    {
      id_t id(100);
    }
    

    zurück zu deinem code:
    typedef type<T, NAME> THIS;
    1. ist THIS nen doofer name für nen typen - wenn überhaupt this_type oder so.
    2. ist die explizite angabe der template-argumente gar nicht nötig. ein einfaches typedef type this_type; würde da reichen.

    const T& _t, const THIS& t
    wieso nicht lhs und rhs, wie das jeder macht sondern ein _t und t? Oo

    wenn die typtrennung strikt sein soll, wieso dann // operator T() const { return x; } ?
    dann schreib doch im private-teil einfach noch operator T() const;

    template<class U> U cast() const { return (U)x; }
    hier sollte man aber echt mit c++ casts arbeiten

    ich nehm mal an, dass es einen grund gibt, wieso man alle operatoren als friend und nicht friend implementiert, obwohl man eh nicht id.operator==(other_id) schreiben kann, gibt!?

    THIS operator + () const { return THIS( x); }
    das hier ist aber echt fail

    eigtl würd ich alle operatoren als template anbieten...
    die streaming-operatoren hast du auch nicht überladen...

    bb



  • Hallo unskilled,

    bei meiner 'type.h' geht es darum, daß z.B. zwei typedef-ints eben nicht als gleich gelten sollen, d.h. bei deinem Wrapper ist

    typedef wrapper<int>::type id_t;
    typedef wrapper<int>::type index_t;
    
    int main()
    {
      id_t id(100);
      index_t index(42);
    
      id = index; // <-- kein Compiler-Fehler!
    }
    

    möglich, und bei meiner Type-Klasse eben nicht!

    Und bzgl. meines Codes:
    diesen habe ich vor ca. 7 Jahren auf einem älteren MSVC compilieren müssen, daher auch einige unschöne Dinge...

    Meine eigentliche 'type.h' sieht heute auch etwas anders aus...

    Aber du darfst sie gerne nach deinen Wünschen aufpolieren und bei boost einreichen -)



  • Th69 schrieb:

    Hallo unskilled,

    bei meiner 'type.h' geht es darum, daß z.B. zwei typedef-ints eben nicht als gleich gelten sollen, d.h. bei deinem Wrapper ist

    das hatte ich schon verstanden 😉

    myself schrieb:

    wenn die typtrennung strikt sein soll, wieso dann // operator T() const { return x; } ?
    dann schreib doch im private-teil einfach noch operator T() const;

    ;o)

    bb



  • Welchen Zweck soll denn der Datentyp haben, vielleicht ist es ja mit einer einfachen enum auch getan?


Anmelden zum Antworten