STL Container in Windows Form

SchindlerD

Grünschnabel
Hallo liebe Forenmitglieder,

ich habe ein Problem mit der Verwendung eines deques in einer Windows Form. Wenn ich versuche, innerhalb der Klasse ein deque zu verwenden, heißt es immer:
Error 1 error C4368: cannot define 'datalist' as a member of managed 'WinMultiFormsTest::Form1': mixed types are not supported
und diverse andere Fehlermeldungen...
Ich verwende übrigens VisualStudio 2005. Wenn ich das deque global deklariere, funktioniert es, wenn ich es als private oder public Member von Form1 einfüge, kommt der Fehler...
Leider verstehe ich nichts von Speicherverwaltung und habe keine Ahnung wie ich dieses Problem lösen soll. Googlen hat mir auch nicht geholfen, wahrscheinlich suche ich falsch
:(
Hier mal der Code:
PHP:
#include <deque>	
			std::deque<struct datastruct> datalist;
			std::deque<struct datastruct>::iterator it;
Und meine Struct:
PHP:
struct datastruct{
	double dt;
	double SHV;
	double SHVloss;			
	double HeP;
	double HePloss;
	};
Noch ein anderes Problem: dt würde ich gerne als DateTime verwenden, leider kommt dann immer:
Error 1 error C3265: cannot declare a managed 'dt' in an unmanaged 'datastruct'
Beispiel:
PHP:
struct datastruct{
	System::DateTime^ dt;
	double SHV;
	double SHVloss;			
	double HeP;
	double HePloss;
	};
Ist es eigentlich arg grausig eine Struct in C++ zu verwenden? Habe bisher nur C programmiert und bin (noch) nicht so gut in C++.
Vielleicht gibt es ja eine einfache Lösung dafür?
Eventuell würde auch eine gute Site helfen, auf der ich mich mit dem managed und unmanaged Thema auseinandersetzen kann... Oder einfach mal ein Beispiel für die Verwendung von so einem Container in einer Form...

Danke jedenfalls schon mal für Eure Hilfe!
Grüße Daniela
 
Hallo Daniela,

Managed und unmanaged Code zusammenzubringen, bringt leider öfters mal unerwartete Probleme.
Daher habe ich bisher auch im wesentlichen lieber die Finger Finger von gelassen :-)

Den ersten Fehler (C4368) kannst du meiner Meinung nach nur umgehen, wenn du das deque-Objekt als Zeiger deklarierst.
Das "DateTime" in deiner Datenstruktur unterzubringen wird dir nur gelingen, wenn du sie als Managed C++ - Klasse deklarierst. Dann gibt's natürlich wieder Ärger, wenn man sie im "deque" unterbringen will, aber das kann man mit "gcroot" lösen.

Hier mal ein kurzer Codeschnipsel; ich hoffe es sind keine gröberen Fehler drin:
C++:
#include <vcclr.h>

// ...

public ref class datastruct
{ 
    System::DateTime ^ dt; 
    double SHV; 
    double SHVloss;             
    double HeP; 
    double HePloss; 
} ; 

public ref class MyForm : public System::Windows::Forms::Form
{
public:
    MyForm(void)
    {
        datalist = new std::deque<gcroot<datastruct^> >;
        it       = new std::deque<gcroot<datastruct^> >::iterator;
    }

protected:
    ~MyForm()
    {
        delete datalist;
        delete it;
    
        if (components)
        {
            delete components;
        }
    }
private:
        std::deque<gcroot<datastruct^> > *datalist; 
        std::deque<gcroot<datastruct^> >::iterator *it;    
        
// ...             
   
};
Ist es eigentlich arg grausig eine Struct in C++ zu verwenden?
Meiner Meinung nach eigentlich nicht, zumindest solange man nicht an Managed C++ gerät :-)

Gruß
MCoder
 
Guten Morgen!

Hätte nicht gedacht, dass das so schnell geht! Vielen Dank für deine Antwort MCoder. Das funktioniert so weit ja schonmal richtig gut! push, pop und Zugriff über front und back geht, allerdings hab ich jetzt ein Problem mit dem Zugriff mittels [].
Mal kurz ein Beispielschnipsel:
C++:
// ...
				System::String^ myString = textBox1->Text;
				double myDouble = System::Convert::ToDouble(myString);
				System::DateTime^ dati = System::DateTime::Now;
				datastruct^ d = gcnew datastruct;
				d->dt = dati;
				d->HeP = myDouble;
				datalist->push_back(d);
// ...
			 datastruct^ d = gcnew datastruct;
				 if (datalist->size() > 0)
				 {
					 d = datalist->back();  // hiermit klappt's
		//			 d = datalist[0];         // hiermit nicht
					 textBox2->Text = System::Convert::ToString(d->dt)+ " " + d->HeP;
				 }
Fehlermeldung beim Kompilieren: error C2440: '=' : cannot convert from 'std::deque<_Ty>' to 'datastruct ^'
Geht das dann nur noch über den Iterator?

Ach ja, den =Operator von datastruct hab ich überladen , vielleicht hab ich da ja was falsch gemacht? Über den back-Zugriff funktioniert es aber super...

C++:
public ref class datastruct
{
public:
double dt;           
double HeP; 
	void operator = (const datastruct^ b)
	{
		dt = b->dt;
		HeP = b->HeP;
	}
};

Also, dann nochmal Danke!
Viele Grüße Daniela
 
Nicht übel!

Naja, ich hab schon fast Angst zu fragen:
Wie funkioniert der Zugriff mit dem Iterator?
Ich habs mal so probiert:
C++:
					 for (it = datalist->begin(); it != datalist->end(); ++it){
						 double a = it->HeP;
					 }
Leider:
error C2440: '=' : cannot convert from 'std::_Deque_iterator<_Ty,_Alloc,_Secure_validation>' to 'std::_Deque_iterator<_Ty,_Alloc,_Secure_validation> *'
und:
error C2679: binary '!=' : no operator found which takes a right-hand operand of type 'std::_Deque_iterator<_Ty,_Alloc,_Secure_validation>' (or there is no acceptable conversion)
zu der Zeile in der double a "gefüllt" wird: error C2039: 'HeP' : is not a member of 'std::_Deque_iterator<_Ty,_Alloc,_Secure_validation>'

Irgendwie komisch, kann doch nicht so schwer sein!
 
Der Iterator ist ja als Zeiger deklariert. Versuch's daher mal so:
C++:
for( *it = datalist->begin(); *it != datalist->end(); ++(*it) )
{
    double a =  ((datastruct^)**it)->HeP;
}
Gruß
MCoder
 
Klappt tatsächlich, danke!
Leuchtet mir teilweise auch ein...
Aber könntest Du vielleicht diese Zeile mal erklären?
C++:
double a =  ((datastruct^)**it)->HeP;
Ist das eine doppelte Dereferenzierung? Auf den normalen Iterator greife ich doch auch nicht mit *it zu?
 
Das kommt daher, weil zum einen der Iterator als Zeiger deklariert und zum anderen "datastruct" mit "gcroot" gekapselt wurde. Das gibt im Ergebnis 2 Sternchen.
Auf den Typecast kann man übrigens verzichten, so reicht's auch:
C++:
double a = (**it)->HeP;
Gruß
MCoder
 
Ist ja interessant. Wird das öfter gemacht? Irgendwie sieht das für mich ... naja ... komisch aus. Solangs funkioniert werd ich mich aber nicht beschweren! ^^
Allerdings hab ich jetzt noch ne Frage zum "Befüllen".

Ich schiebe in einer Schleife Werte in das deque, die ich aus einer CSV-datei ausgelesen habe. Als das deque noch global war, hat das so funktioniert:
C++:
//...
			datastruct a;
			System::IO::StreamReader^ msr;
			System::String^ str;
			array<System::String^,1>^ ain;
//...
			msr = gcnew System::IO::StreamReader(::Application::StartupPath+"\\Log.csv");
//...

							for (int j = 0; j< 165; j++){		
								str = msr->ReadLine();
								ain = str->Split(';');
								a.SHV = StringToDouble((ain[1])->Trim());
								a.dt = (Convert::ToDateTime((ain[2])->Trim())).ToOADate();
								a.HeP = StringToDouble((ain[3])->Trim());
								datalist.push_back(a);
}
/...

Da ich jetzt aber nur ne Referenz übergebe kann ich ja das a nicht mehr am Anfang der Funkion initialisieren (sonst ändern sich ja immer alle Werte im Deque auf diejenigen, auf die a gerade zeigt!). Also habe ich die Initialisierung von a in die Schleife gelegt. Funktioniert ohne Probleme, nur würde ich jetzt gerne wissen, ob das irgendwie verwerflich ist, da ich doch durch die For-Schleife dauernd Zeiger lösche, die ich noch brauche! Wird im deque jetzt tatsächlich der Zeiger gespeichert oder die Werte, die darunter liegen? (Können die Speicherzellen, deren Inhalt ich beim Debuggen in dem deque angezeigt kriege, überschrieben werden?)
Hier mal ein paar Auszüge aus meinem aktuellen Code:
C++:
			System::IO::StreamReader^ msr;
			array<System::String^,1>^ ain;
			System::String^ str;
			datastruct^ b = gcnew datastruct;
/...
					if ( System::IO::File::Exists(::Application::StartupPath+"\\Log.csv") )
					{
						msr = gcnew System::IO::StreamReader(::Application::StartupPath+"\\Log.csv");
			
			
							for (int j = 0; j< (int)aSHVzero; j++){	
								datastruct^ a = gcnew datastruct;
								str = msr->ReadLine();
								ain = str->Split(';');

								a->SHV = StringToDouble((ain[SHV])->Trim());
								a->dt = (Convert::ToDateTime((ain[datetime])->Trim())).ToOADate();
								a->HeP = StringToDouble((ain[HeP])->Trim());
							
								datalist->push_back(a);
							}						
						}

/...
							msr->Close();
					}
 
Wird das öfter gemacht?
Keine Ahnung. Um mit unmanaged Code in Managed C++ zu arbeiten, muss man wohl etwas kreativ werden :-)
Es wäre auch eine Überlegung wert, auf die STL-Klassen (wenn möglich) zu verzichten und stattdessen .NET-Klassen zu verwenden. Bei "System::Collections" gibt es da einiges an Arrays und Listen.

Funktioniert ohne Probleme, nur würde ich jetzt gerne wissen, ob das irgendwie verwerflich ist, da ich doch durch die For-Schleife dauernd Zeiger lösche, die ich noch brauche!
Du legst zwar immer wieder eine neue Referenz an, aber du löschst die Bisherigen nicht. Das Löschen übernimmt irgendwann mal der Garbage Collector, wenn die Referenz nirgendwo mehr verwendet wird (hoffentlich auch erst dann). Im deque werden die Zeiger gespeichert, das Ablegen von Managed-Objekten ist ja nicht möglich.

Gruß
MCoder
 
Zurück