Loeschen von doppelt vorhandenen Typen in Typelist geht nicht



  • Hallo
    Jetzt habe ich ein Problem mit einem Typlisten Algorithmus: der Algorithmus "NoDuplicates" sollte alle doppelten Typen in einer Typelist entfernen. Das tut er in meinem Fall auch. Mehr oder weniger. Nur ersetzt er das zweite Aufkommen von "unsigned char" (dem doppelt vorhandenen Typ, wegen dem Replace vorher) nicht mit dem Nulltype oder loescht ihn ganz einfach aus der Liste (gewuenschtes Verhalten). Nein, er ersetzt ihn seltsamerweise mit "unsigned int"?!? Die Typliste wird auch nicht kuerzer (auch schon nachgetestet).

    Der dazu erstellte Code von mir ist sehr an die Implementierung in Alexandrescu's Library "Loki" angelehnt http://loki-lib.sourceforge.net/ Das Programm sollte problemlos kompilieren und laufen. Die Version ist wegen des hier einigermassen internationalen Umfeldes in englisch, ich hoffe, das is ok 😉

    Frage(n):
    Warum loescht NoDuplicates die entsprechenden doppelten Typen nicht einfach aus der Typelist, sondern ersetzt sie?
    Warum werden sie durch "unsigned int" ersetzt?

    Danke im voraus, falls jemand hierfuer die Geduld faende. 🙄

    #include <iostream>
    #include <typeinfo>
    
    /*
      The null type
    //*/
    class NullType
    {};
    
    /*
      The typelist itself
    //*/
    template< class T, class U >
    struct Typelist_
    {
      typedef T 
        Head;
    
      typedef U 
        Tail;
    };
    
    /*
      Linearizing Typelist Creation
    
      ...a better alternative would use templates
    //*/
    
    // declaration for 4 elements 
    template< class T1, class T2 = NullType, class T3 = NullType, class T4 = NullType >
    struct Typelist
    {
      typedef Typelist_< T1, Typelist_< T2, Typelist_< T3, Typelist_< T4, NullType > > > > 
        type_t;
    };
    
    // definitions.. 
    template< class T1 >
    struct Typelist< T1, NullType, NullType, NullType >
    {
      typedef Typelist_< T1, NullType > 
        type_t;
    };
    
    template< class T1, class T2 >
    struct Typelist< T1, T2, NullType, NullType >
    {
      typedef Typelist_< T1, Typelist_< T2, NullType > > 
        type_t;
    };
    
    template< class T1, class T2, class T3 >
    struct Typelist< T1, T2, T3, NullType >
    {
      typedef Typelist_< T1, Typelist_< T2, Typelist_< T3, NullType > > > 
        type_t;
    };
    
    /*
      Typelist algorithms live in the TL namespace
    //*/
    namespace TL
    {
      /*
        Indexed Access
    
        The declaration of a template for an indexed operation would look like this:
          template< class TList, unsigned int index > struct TypeAt;
    
        Algorithm:
        IF TList is non-null and i is zero, then Result is the head of TList
        ELSE
          IF TList is non-null and index i is nonzero, then Result is obtained by applying
            TypeAt to the tail of TList and i-1
          ELSE there is an out-of-bound access that translates into a compile-time error
    
        Usage:
        TL::TypeAt< MyTypelist, idx >::Result variable;
      //*/
    
      // basic template form
      template< class TList, unsigned int index > 
      struct TypeAt;
    
      // head
      template< class Head, class Tail > 
      struct TypeAt< Typelist_< Head, Tail >, 0 >
      {
        typedef Head 
          Result;
      };
    
      // any element
      template< class Head, class Tail, unsigned int i >
      struct TypeAt< Typelist_< Head, Tail >, i>
      {
        typedef typename TypeAt< Tail, i-1 >::Result 
          Result;
      };
    
      /*
        Erasing a Type from a Typelist
    
        Algorithm:
        IF TList is NullType, then Result is NullType
        ELSE 
          IF T is the same as TList::Head, then Result is TList::Tail
          ELSE Result is a typelist having TList::Head as its head 
            and the result of applying Erase to TList::Tail and T as its tail
    
        Usage:
        typedef Erase< MyTypelist, TypeToErase >::Result MyNewTypelist;    
      //*/
      template< class TList, class T > 
      struct Erase; 
    
      template< class T >
      struct Erase< NullType, T >
      {
        typedef NullType 
          Result;
      };
    
      template< class T, class Tail >
      struct Erase< Typelist_< T, Tail >, T >
      {
        typedef Tail 
          Result;
      };
    
      template< class Head, class Tail, class T >
      struct Erase< Typelist_< Head, Tail > , T >
      {
        typedef Typelist_< Head, typename Erase< Tail, T >::Result >
          Result;
      };
    
      /*
        No Duplicates
    
        Algorithm:
        IF TList is NullType, then Result is NullType
        ELSE
          Apply NoDuplicates to TList::Tail, obtaining a temporary typelist L1
          Apply Erase to L1 and TList:Head. Obtain L2 as the result
          Result is a typelist whose head is TList::Head and whose tail is L2
    
        Usage:
        typedef TL::NoDuplicates< MyTypelist >::Result MyTypelistWithoutDuplicates;
      //*/
      template< class TList > 
      struct NoDuplicates;
    
      template<>
      struct NoDuplicates< NullType >
      {
        typedef NullType 
          Result;
      };
    
      template< class Head, class Tail >
      struct NoDuplicates< Typelist_< Head, Tail > >
      {
      private:
        typedef typename NoDuplicates< Tail >::Result 
          L1;
    
        typedef typename Erase< L1, Head >::Result 
          L2;
    
      public:
        typedef Typelist_< Head, L2 > 
          Result;
      };
    
      /*
        Replacing an Element in Typelist
    
        Algorithm:
        IF TList is NullType, then Result is NullType
        ELSE 
          IF the head of the typelist TList is T, then Result is a typelist 
            with U as its head and TList::Tail as its tail
          ELSE Result is a typelist with TList::Head as its head and the 
            result of applying Replace to TList, T, and U as its tail.
    
        Usage:
        typedef TL::Replace< MyTypelist, TypeToRemove, TypeToReplace >::Result MyReplacedTypelist;
      //*/  
      template< class TList, class T, class U >
      struct Replace;
    
      template< class T, class U >
      struct Replace< NullType, T, U >
      {
        typedef NullType 
          Result;
      };
    
      template< class T, class Tail, class U >
      struct Replace< Typelist_< T, Tail >, T, U >
      {
        typedef Typelist_< U, Tail > 
          Result;
      };
    
      template< class Head, class Tail, class T, class U >
      struct Replace< Typelist_< Head, Tail >, T, U >
      {
        typedef Typelist_< Head, typename Replace< Tail, T, U >::Result> 
          Result;
      };
    }
    
    /*
      main..
    //*/
    int main()
    {
      using namespace std;
    
      // init a TYPELIST_3 - old style: avoid MACROs!
      cout << "init a typelist\n";
      typedef Typelist< unsigned char, unsigned short int, unsigned int >::type_t 
        MyTypelist_t;
      cout << endl;
    
      // replace elements
      cout << "REPLACE ELEMENTS\n";
      cout << "second element is \"unsigned int\"?\t: " 
           << ((typeid(unsigned int) == typeid(TL::TypeAt< MyTypelist_t, 2 >::Result)) ? "true" : "false") 
           << endl;
      cout << "replacing elmenet \"unsigned int\" with \"unsigned char\"...";
      typedef TL::Replace< MyTypelist_t, unsigned int, unsigned char >::Result 
        MyReplacedTypelist_t;  
      cout << "done.\n";
      cout << "now second element is \"unsigned char\"?\t: " 
           << ((typeid(unsigned char) == typeid(TL::TypeAt< MyReplacedTypelist_t, 2 >::Result)) ? "true" : "false") 
           << endl;  
      cout << endl;
    
      // erase duplicates 
      cout << "ERASE DUPLICATES\n";
      cout << "content before:\n"
           << "\t\"unsigned char\"\t\t: " 
           << ((typeid(TL::TypeAt< MyReplacedTypelist_t, 0 >::Result) == typeid(unsigned char)) ? "true" : "false") 
           << endl
           << "\t\"unsigned short int\"\t: " 
           << ((typeid(TL::TypeAt< MyReplacedTypelist_t, 1 >::Result) ==  typeid(unsigned short int)) ? "true" : "false") 
           << endl
           << "\t\"unsigned char\"\t\t: " 
           << ((typeid(TL::TypeAt< MyReplacedTypelist_t, 2 >::Result) == typeid(unsigned char)) ? "true" : "false")
           << endl;
      cout << "do no duplicates...";
      typedef TL::NoDuplicates< MyTypelist_t >::Result 
        MyTypelistWithoutDuplicates_t;
      cout << "done.\n";
      cout << "content after:\n"
           << "\t\"unsigned char\"\t\t: " 
           << ((typeid(TL::TypeAt< MyTypelistWithoutDuplicates_t, 0 >::Result) == typeid(unsigned char)) ? "true" : "false") 
           << endl
           << "\t\"unsigned short int\"\t: " 
           << ((typeid(TL::TypeAt< MyTypelistWithoutDuplicates_t, 1 >::Result) ==  typeid(unsigned short int)) ? "true" : "false") 
           << endl
           << "\t\"unsigned char\"\t\t: " 
           << ((typeid(TL::TypeAt< MyTypelistWithoutDuplicates_t, 2 >::Result) == typeid(unsigned char)) ? "true" : "false")
           << endl;
    
      cout << "\nPROBLEM: replaced by \"unsigned int\"\n"
           << "\"unsigned int\"\t\t: " 
           << ((typeid(TL::TypeAt< MyTypelistWithoutDuplicates_t, 2 >::Result) == typeid(unsigned int)) ? "true" : "false")
           << endl;
      cout << endl;
    
      cout << "READY.\n";
      return 0;
    }
    


  • *push*


  • Administrator

    ACHTUNG! DU WIRST DICH ÄRGERN!

    typedef TL::NoDuplicates< MyTypelist_t >::Result MyTypelistWithoutDuplicates_t;
    

    Sollte wohl eher so lauten:

    typedef TL::NoDuplicates< MyReplacedTypelist_t >::Result MyTypelistWithoutDuplicates_t;
    

    Sonst wäre das Resultat absolut korrekt. Das kommt von schlechter Übersicht in deiner Mainfunktion. Gib deinem Code mehr platz, lagere Dinge in Funktionen aus. Zum Beispiel so ein Code könnte dir auch weiterhelfen, hat es zumindest mir:

    template<typename TList>
    struct DoOutput;
    
    template<typename Head>
    struct DoOutput<Typelist_<Head, NullType> >
    {
    	DoOutput()
    	{
    		std::cout << typeid(Head).name() << std::endl;
    	}
    };
    
    template<typename Head, typename Tail>
    struct DoOutput<Typelist_<Head, Tail> >
    {
    private:
    	typedef DoOutput<Tail> NextOutput;
    
    public:
    	DoOutput()
    	{
    		std::cout << typeid(Head).name() << std::endl;
    		NextOutput();
    	}
    };
    
    // Verwendung:
    DoOutput<MyTypelist_t>();
    

    Dann steht in der Mainfunktion zum Beispiel nur noch das folgende:

    int main() 
    {
    	typedef Typelist< unsigned char, unsigned short int, unsigned int >::type_t MyTypelist_t;
    
    	DoOutput<MyTypelist_t>();
    	std::cout << std::endl;
    
    	typedef TL::Replace< MyTypelist_t, unsigned int, unsigned char >::Result MyReplacedTypelist_t;
    
    	DoOutput<MyReplacedTypelist_t>();
    	std::cout << std::endl;
    
    	typedef TL::NoDuplicates< MyReplacedTypelist_t >::Result MyTypelistWithoutDuplicates_t;
    
    	DoOutput<MyTypelistWithoutDuplicates_t>();
    	std::cout << std::endl;
    
    	test::wait("PAUSE");
    
    	return 0;
    }
    

    Grüssli



  • Super, danke - aergern.. nicht wirklich 😉

    Nja, der Code wurde irgendwie zunaechst fuer andere Dinge benutzt, dann kam dieses Problem auf, dann wollte ich auf Laenge testen, dann doch die Types direkt vergleichen, dann dies, dann jenes... schliesslich kam obiger zusammengeflickter Code heraus, mit einer entsprechenden Unleserlichkeit die selbst einfache Schreibfehler unkenntlich macht. Ich denke das ist v.a. bei der Arbeit mit Templates und deren ausschweifender Schreibweise ein Riesenfallstrick oder zumindest kann es sehr nervig sein und man muss um so mehr aufpassen die Uebersicht zu bewahren. Danke! Wird mir eine Lehre sein, hoffe ich!! 🙂


Log in to reply