Generics und Konvertierung



  • Moin,

    ich habe mal eine Frage zum o.g. Thema. Die folgende Funktion befindet sich in einer CLR Class Library.

    #pragma once
    
    namespace MyNameSpace
    {	
    	public ref class Converter
    	{
    		public:
    
    			generic <typename T> static void zero( array<T>^ src)
    			{
    				for( unsigned int x=0 ; x < static_cast<unsigned int>(src->Length) ; x++)
    					src[x] = safe_cast<T>(0);	
    			}			
    	};
    }
    

    Durch den Compiler läuft alles wunderbar durch, auch lässt sich ein Testaufruf starten. Aber beim Versuch die 0 nach <T> zu casten fliegt der Testaufruf mit einer InvalidCastException raus.

    Anzumerken ist noch, dass <T> im Grunde nur numerische Typen darstellt, sprich byte, int, float, double etc.

    Kann mir jemand einen Tip geben, wie man das Problem löst ?

    Danke ! 🙂



  • Hier mal ein vollständiges Beispiel, was wunderbar funktioniert...

    namespace MyNameSpace 
    {   
        public ref class Converter 
        { 
            public: 
    
                generic <typename T> static void zero( array<T>^ src)
                { 
                    for( unsigned int x=0 ; x < static_cast<unsigned int>(src->Length) ; x++)
                         src[x] = safe_cast<T>(0);   
                }           
        }; 
    }
    
    int main(array<System::String ^> ^args)
    {
        array<int>^ arr = { 1, 2, 3, 4};
        for each (int var in arr)
        {
            System::Console::WriteLine(var);
        }
        MyNameSpace::Converter::zero(arr);
        for each (int var in arr)
        {
            System::Console::WriteLine(var);
        }
        return 0;
    }
    


  • Erst einmal danke für Deine Antwort !

    Aber ich komme da wirklich nicht weiter. Wie gesagt, die Funktion liegt in einer CLR Class Library. Diese habe ich als Referenz in ein C# Projekt eingebunden.

    Mein Aufruf aus der C# Welt sieht dann etwas so aus.

    byte[] src = new byte[512];
    
    Converter.zero(src);
    

    Ich habe das Gefühl, dass ich etwas grundlegendes falsch mache, aber was ? 😕



  • Jetzt wird es ganz konfus 😕

    Habe das ganze einmal mit anderen Typen, wie in Deinem Beispiel ausprobiert.

    Es funktioniert mit int[] und float[], aber nicht mit short[] und byte[]



  • Mach das Beispiel doch mal in C#, dann siehst Du Dein Problem!

    Du hast T nicht genauer spezifiziert. Was Du eigentlich willst ist ja, dass das Array den "Default"_Wert bekommt (also 0).

    Du solltest also Folgendes machen:

    namespace MyNameSpace 
    {   
        public ref class Converter 
        { 
            public: 
    
                generic <typename T> static void zero( array<T>^ src)
                { 
                    for(int x=0 ; x < src->Length; x++)
                         src[x] = T();   
                }           
        }; 
    }
    
    int main(array<System::String ^> ^args)
    {
        array<short>^ arr = { 1, 2, 3, 4};
        for each (int var in arr)
        {
            System::Console::WriteLine(var);
        }
        MyNameSpace::Converter::zero(arr);
        for each (int var in arr)
        {
            System::Console::WriteLine(var);
        }
        return 0;
    }
    


  • Ich verstehe das immer noch nicht. Dein zweites Beispiel mit Aufruf des Konstruktors von T() funktioniert.

    Offene Fragen sind immer noch:

    - warum hat es vorher mit float und int funktioniert, aber nicht mit byte und short
    - wie würde die Zuweisung aussehen wenn statt 0 ein Wert z.B. 5 geschrieben werden soll

    Zumindest habe ich schon einmal gelernt dass generics scheinbar nicht gleich der c++ templates sind, bedauerlicherweise.

    Könnte ich eine Funktion wie die folgende überhaupt nach C++/CLR überführen, die dann von der C# Seite aus mit array<T> als Parameter benutzt werden kann ?

    template <typename S> static void Normalize( S* src, float* dst, unsigned int size)
    {
    	float n = static_cast<float>(1 << ( 8 * sizeof(S) - 1 ));
    
    	for( unsigned int x=0; x<size; x++)
    		dst[x] = static_cast<float>(src[x]) / n;
    
    	return;
    }
    

    Danke für Deine Mühe !



  • Du musst Dir darüber im klaren sein, das Generic vollkommen anders sind als Templates!

    Der primäre Unterschied ist: genric müssen zur Compilezeit, ohne den konkreten Typ zu kennen, kompilierbar sein! Templates werden erst "vollständig" kompiliert, wenn die passende Ausprägung bekannt ist!

    Deswegen musst Du bei Generics mit passenden Einschränkungen arbeiten, die es dann ermöglicht das zu machen was Du willst. Wie der Name aber sagt, schränkt dies ein...

    Zu Deinem Beispiel: Ich würde dies in C# so machen:

    static void Normalize<T>(T[] src, out float[] dst) where T : IConvertible
            {
                if (src == null)
                    throw new ArgumentNullException("src");
                dst = new float[src.Length];
                int sizeOfT = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
                float n = (1 << (8 * sizeOfT - 1));
    
                for (int x = 0; x < src.Length; x++)
                {
                    float flt = src[x].ToSingle(System.Globalization.CultureInfo.InvariantCulture);
                    dst[x] = flt / n;
                }
            }
    
            [STAThread]
            static void Main(string[] args)
            {
                int[] arr = { 1, 3, 7 };
                float[] res;
                Normalize(arr, out res);
                Array.ForEach(res, p => Console.WriteLine(p));
    
            }
    


  • 1000 Dank für Deine guten Beispiele. Das letzte hat mir wirklich sehr geholfen und ich habe zumindest meine paar Funktionen direkt nach C# portieren können.

    Mir ist allerdings immer noch nicht klar, wie die Lösung in einer C++/CLR Umgebung aussehen würde, wenn das denn überhaupt möglich ist.

    👍



  • Ich verstehe Dich nicht ganz... man setzt es einfach um...

    public:
            generic <typename T> where T : System::IConvertible
            static void NormalizeT(array<T>^ src, array<float>^ dst)
            { 
                if (src == nullptr) 
                    throw gcnew ArgumentNullException("src"); 
                dst = gcnew array<float>(src->Length); 
                int sizeOfT = System::Runtime::InteropServices::Marshal::SizeOf(T::typeid);
                float n = (1 << (8 * sizeOfT - 1)); 
    
                for (int x = 0; x < src->Length; x++) 
                { 
                    float flt = src[x].ToSingle(System::Globalization::CultureInfo::InvariantCulture);
                    dst[x] = flt / n; 
                } 
            }
    

    Beachte aber bitte auch:
    http://blog.kalmbach-software.de/de/2010/03/05/ccli-und-winforms-macht-keinen-sinn/


Anmelden zum Antworten