Bestimmung des (Result)-Typs eines Pointer-To-Member via Templates



  • mach folgendes:

    a)Definiere ein Traits-Template für die Strukturen, wo du den Subtype als typedef drin hast.
    b1) Wenn du die Strukturen selber definierst/generierst, packe noch ein typedef für die subtypen rein, im Traits geht das typedef dann auf den typedef im struct.
    b2) Für fremddefinierte Strukturen spezialisierst du den trait

    template <class Type>
    struct MemarrayTraits
    {
      typedef Type::subtype subtype; //default: das struct definiert selber den subtype
    };
    
    template< typename Type >
    struct ArrayWrapper
    {
        typedef MemarrayTraits<Type>::subtype Subtype; //die traits bestimmen den subtype
        ArrayWrapper( Type*& s ) : m_struct( s ) {}
    
        Subtype& value( size_t index ) { return m_struct->item[ index ]; }
    
    private:
        Type*& m_struct;
    };
    
    // Strukturen
    struct MyOwnArrayOfX //selbstgeschrieben
    {
       size_t size;
       X* array;
       typedef X subtype; 
    };
    
    struct TheirArrayOfY //fremdgeschrieben...
    {
        size_t size;
        Y* array;
    };
    
    //... daher Spezialisierung des traits, weil das struct den subtype nicht definiert
    templyte<>
    struct MemarrayTraits<TheirArrayOfY>
    {
      typedef Y subtype;
    };
    


  • Badestrand schrieb:

    BOOST_TYPEOF?

    Und das hilft bei Template-Argumenten genau wie?



  • pumuckl schrieb:

    ...

    Das wäre die krampfige Methode, von der ich schrieb. Das wird aber ziemlich ätzend, wenn es sehr viele generierte Strukturtypen gibt.



  • Tachyon schrieb:

    pumuckl schrieb:

    ...

    Das wäre die krampfige Methode, von der ich schrieb. Das wird aber ziemlich ätzend, wenn es sehr viele generierte Strukturtypen gibt.

    Wenn es einen weg gibt, die Strukturtypen zu generieren, dann wirds auch einen Weg geben, die Trait-Spezialisierungen zu generieren 😉
    Oder eben den generator umschreiben dass er das typedef noch mit dazubaut.



  • Tachyon schrieb:

    Badestrand schrieb:

    BOOST_TYPEOF?

    Und das hilft bei Template-Argumenten genau wie?

    Gnarg, scheint echt nicht zu klappen..



  • pumuckl schrieb:

    Tachyon schrieb:

    pumuckl schrieb:

    ...

    Das wäre die krampfige Methode, von der ich schrieb. Das wird aber ziemlich ätzend, wenn es sehr viele generierte Strukturtypen gibt.

    Wenn es einen weg gibt, die Strukturtypen zu generieren, dann wirds auch einen Weg geben, die Trait-Spezialisierungen zu generieren 😉
    Oder eben den generator umschreiben dass er das typedef noch mit dazubaut.

    Also wir haben hier einen Generator, der aus einem IDL-Subset Code für unterschiedliche Sprachen generiert (u.a. C) generiert. Der weiss nichtmal, dass es Templates gibt. Erweiterungen sind da auch nicht denkbar.

    Wenn man sowas privat gebastelt hat, klar. Dann kann man das erweitern wie man lustig ist.

    Aber in Firmen, wo die Tools gesetzt sind? Wohl eher nicht.



  • Falls ein Makro anstatt einer Funktion in Ordnung ist:

    #include <boost/typeof/typeof.hpp>
    
    template<class T>
    struct ExtractMemberType;
    
    template<class T, class E>
    struct ExtractMemberType<E* T::*>
    {
    	typedef E type;
    };
    
    struct X;
    struct ArrayOfX
    {
    	X* array;
    };
    
    template< typename Type, typename Subtype >
    struct ArrayWrapper
    {
    	ArrayWrapper( Type*& )
    	{
    	}
    };
    
    #define ArrayWrapperType(s)  ArrayWrapper< BOOST_TYPEOF(*s), ExtractMemberType<BOOST_TYPEOF( &BOOST_TYPEOF(*s)::array )>::type >
    #define ArrayOf(s)  ArrayWrapper< BOOST_TYPEOF(*s), ExtractMemberType<BOOST_TYPEOF( &BOOST_TYPEOF(*s)::array )>::type > (s)
    // edit: bzw  #define ArrayOf(s)  ArrayWrapperType(s)(s)
    
    template<typename Type>
    void foo( Type* s )
    {
    	ArrayWrapperType(s) asd = ArrayOf( s );
    
    	std::cout << typeid(ArrayWrapperType(s)).name() << "\n"; // -> struct ArrayWrapper<struct ArrayOfX,struct X>
    }
    
    int main()
    {
    	ArrayOfX* s = NULL;
    
    	foo( s );
    }
    


  • Naja, Sinn der Sache ist ja, möglichst nicht die aussenstehenden Beteiligten (sprich den Generator) zu modifizieren. Der ist nämlich tatsächlich nicht von uns.

    Mit BOOST_TYPEOF würde es sogar funktionieren:

    template< typename Type >
    struct result_type_helper
    {
        static Type* t;
        typedef BOOST_TYPEOF( t->item ) type;
    };
    
    template< typename Type >
    struct ArrayWrapper
    {
        typedef typename result_type_helper< Type >::type Subtype;
    
        /* ... */
    };
    

    Jetzt ist das Problem, dass einer der Compiler hier keine Typeof-Erweiterung kennt, und damit alle Typen wenn ich das richtig verstanden habe "registriert" werden müssen. Das kommt mehr oder weniger der Lösung von pumuckl gleich.

    Den Makro-Vorschlag hab ich jetzt noch nicht angeschaut, Beitrag wurde vorher begonnen 🙂



  • LordJaxom schrieb:

    Naja, Sinn der Sache ist ja, möglichst nicht die aussenstehenden Beteiligten (sprich den Generator) zu modifizieren. Der ist nämlich tatsächlich nicht von uns.

    Etwas was mir noch eingefallen ist: Wenn die Structs generiert werden, sidn sie alle nach Schema F gestrickt. Die entsprechende Datei(en) sind auch leicht zu finden. Man könnte dann einen zusätzlichen Generator schreiben, der die "Registrierung" (d.h. Spezialisierung des Traits/Helpertemplates) vornimmt indem er die generierten Dateien parst und die Spezialisierungen schreibt - das parsen ist schnell erledigt, der Generator wäre ein recht kurzes Script, das man problemlos in jeden Buildprozess einbauen kann.


  • Administrator

    #include <boost/type_traits.hpp>
    
    struct ArrayOfX
    {
    	std::size_t n;
    	int* array;
    };
    
    template<typename X>
    struct GetMemberType;
    
    template<typename C, typename M>
    struct GetMemberType<M C::*>
    {
    	typedef M Type;
    };
    
    template<typename T, typename SubT>
    struct ArrayWrapper
    {
    	ArrayWrapper(T*& s)
    		: m_struct(s)
    	{
    	}
    
    	SubT& value(size_t index)
    	{
    		return m_struct->array[index];
    	}
    
    private:
    	T*& m_struct;
    };
    
    template<typename T, typename X>
    ArrayWrapper
    <
    	T,
    	typename boost::remove_pointer<typename GetMemberType<X>::Type>::type
    >
    ArrayOf(T*& s, X)
    {
    	return
    	ArrayWrapper
    	<
    		T,
    		boost::remove_pointer<typename GetMemberType<X>::Type>::type
    	>
    	(s);
    }
    
    int main()
    {
    	ArrayOfX obj;
    	ArrayOfX* p = &obj;
    
    	ArrayOf(p, &ArrayOfX::array);
    
    	return 0;
    }
    

    Ohne &ArrayOfX::array habe ich es bisher noch nicht hinbekommen. Man könnte diesen Zeiger allerdings benutzen, falls es abweichende Namen gibt (*). Grundsätzlich könnte die aktuelle Lösung mehr als du verlangst. Sie um diese zusätzlichen Möglichkeiten zu verringern, dass ist mir halt noch nicht gelungen 🙂

    GetMemberType und boost::remove_pointer könnte man irgendwie noch zusätzlich verknüpfen, falls du möchtest. Ich war jetzt zu faul, dies noch hinzuschreiben.

    Wieso hälst du eigentlich eine Referenz auf einen Zeiger im ArrayWrapper ? Ist nicht so günstig um einen operator = vom Kompiler generieren zu lassen 😉

    Edit:
    * Hier noch der angepasste Code, falls man abweichende Namen hat. Verändert wurden ArrayWrapper und ArrayOf :

    template<typename T, typename SubT>
    struct ArrayWrapper
    {
    	typedef typename boost::remove_pointer<SubT>::type value_type;
    
    	ArrayWrapper(T* s, SubT T::*ptrToMember)
    		: m_struct(s)
    		, m_ptrToMember(ptrToMember)
    	{
    	}
    
    	value_type& value(size_t index)
    	{
    		return (m_struct->*m_ptrToMember)[index];
    	}
    
    private:
    	T* m_struct;
    	SubT T::* m_ptrToMember;
    };
    
    template<typename T, typename X>
    ArrayWrapper
    <
    	T,
    	typename GetMemberType<X>::Type
    >
    ArrayOf(T* s, X ptrToMember)
    {
    	return
    	ArrayWrapper
    	<
    		T,
    		typename GetMemberType<X>::Type
    	>
    	(s, ptrToMember);
    }
    

    Grüssli



  • Dravere schrieb:

    Wieso hälst du eigentlich eine Referenz auf einen Zeiger im ArrayWrapper ? Ist nicht so günstig um einen operator = vom Kompiler generieren zu lassen 😉

    Das ist im Zuge der Verkürzung reingekommen - im Real World Code ist es ein Doppelzeiger 🙂

    Die Idee mit dem Memberzeiger hatte ich auch schon, da bräuchte man sogar kein Helfertemplate mehr - inkl. dem Versuch, &T::array als Defaultparameter übergeben zu lassen, was der Compiler aber nicht wirklich mochte. Ist aber auch irgendwie klar, wie soll der Compiler auch den zweiten Templateparameter herleiten, bevor das dazugehörige Argument bekannt ist.

    /* Verkürzte Version ohne Helper */
    struct X
    {
        int wert;
    };
    
    struct ArrayOfX
    {
        size_t size;
        X* array;
    };
    
    template< typename Struct, typename Type >
    struct ArrayWrapper
    {
    };
    
    template< typename Struct, typename Type >
    ArrayWrapper< Struct, Type > ArrayOf( Struct*& capsule, Type* ( Struct::*array ) /* = &Struct::array */ )
    // das Default-Argument wird, wenn vorhanden, nichtmals angemeckert. Nur ist es kein wirklicher Default, weil der Aufruf mit einem Parameter nicht funktioniert.
    {
        return ArrayWrapper< Struct, Type >();
    }
    
    int main()
    {
        ArrayOfX* capsule = 0;
        ArrayOf( capsule, &ArrayOfX::array );
    }
    

    Da wäre mir dann aber die Syntax ArrayOf< X >( capsule ); für den Standardfall und ArrayOf( capsule, &ArrayOfX::abweichenderName ); für den Fall abweichender Namen lieber:

    // Parameter vertauscht, wegen ArrayOf< Type >
    template< typename Type, typename Struct >
    ArrayWrapper< Struct, Type > ArrayOf( Struct*& capsule, Type* ( Struct::*array ) = &Struct::array )
    {
        return ArrayWrapper< Struct, Type >();
    }
    

    Ich lasse mir aber tatsächlich auch nochmal die Möglichkeit eines Generators für eine automatische Traits-Erstellung durch den Kopf gehen.


Anmelden zum Antworten