Etwas verwirrt mit Headerdateien und weiterreichen von Variablen/Strings



  • conqueror24 schrieb:

    Das habe ich schon gemacht, ich glaube aber ich stolpere über den Syntax. Das std::string in der clac.h war offensichtlich falsch, für mein Vorhaben eine Funktion in die calc.cpp zu tun, die dann einen String zurückliefert.

    Wo kann ich das gut nachlesen, wie das mit der Deklaration am besten funktioniert? Ich habe schon einige Tutorials versucht, aber in keinem was dazu gefunden. Gibts wo einen Beispielcode zufälligerweise?

    Zeige doch einfach mal dein Form1.h.

    Ich denke, es ist nicht verkehrt std::string in calc.h zu verwenden - das Problem ist, dass die RichTextBox einen System::String verwendet (noch dazu ein Ref Handle). Das heisst Du musst zwischen diesem beiden Typen convertieren.

    http://www.c-plusplus.net/forum/158664

    EDIT:
    Hier noch ein Bsp. inkl. verwendung von globalen Variabeln - sollte man nicht tun kommt aber deinem Problem näher.

    Form1.h

    #pragma once
    
    #include <msclr\marshal_cppstd.h>
    using namespace msclr::interop;
    
    #include "Calc.h"
    
    namespace Test {
    
    	using namespace System;
    	using namespace System::ComponentModel;
    	using namespace System::Collections;
    	using namespace System::Windows::Forms;
    	using namespace System::Data;
    	using namespace System::Drawing;
    
    	/// <summary>
    	/// Summary for Form1
    	/// </summary>
    	public ref class Form1 : public System::Windows::Forms::Form
    	{
                    // ... nicht relevante Dinge rausgenommen...
    
        private: System::Void calcButton_Click(System::Object^  sender, System::EventArgs^  e) {
    
                     String^ s = richTextBox->Text;
    
                     calc::text = marshal_as<std::string>(s);
    
                     calc::f();
    
                     richTextBox->Text = gcnew String(calc::text.c_str());
                 }
        };
    }
    

    Calc.h

    #pragma once
    
    #include <string>
    
    namespace calc
    {
        extern std::string text;
    
        void f();
    }
    

    Calc.cpp

    #include "stdafx.h"
    #include "Calc.h"
    #include <algorithm>
    
    namespace calc
    {
        std::string text;
    
        void f()
        {
            std::reverse(begin(text), end(text));
        }
    }
    


  • Danke erstmal für deine Mühe, hier ausschnittweise meine form1.h (ich poste mal nicht alles, weil ich vermute dass das nur Leseaufwand aber keine Information mehr bringt:

    form1.h:

    private: System::Void mach_was_Click(System::Object^  sender, System::EventArgs^  e) {
    
    			String^ Inhalt_richTextBox1 = richTextBox1->Text;
    			MessageBox::Show(aendern(Inhalt_richTextBox1));
    
    			 }
    

    calc.h:

    #pragma once
    
    #ifndef CALC_H
    #define CALC_H
    
    #include <string>
    
    namespace diese_und_jene_Variable	{
    
    	extern int block_whs;
    	extern int block_abstand;
    	extern int zeichen_anz;
    	extern std::string Inhalt_richTextBox1;
    }
    
    #endif
    

    calc.cpp:

    #include "stdafx.h"
    #include <sstream>
    #include <string>
    #include <cmath>
    #include "calc.h"
    
    using namespace std;
    
    namespace diese_und_jene_Variable	{
    
    	// Anfangswerte festsetzen
    
    	int block_whs = 0;
    	int block_abstand = 0;
    	int zeichen_anz = 0;
    	std::string Inhalt_richTextBox1;
    }
    
    std::string aendern(std::string Inhalt_richTextBox1)	{
    	using namespace diese_und_jene_Variable;
    	Inhalt_richTextBox1 = "HALLO";
    return Inhalt_richTextBox1;}
    

    Was ich an deinem Code nicht ganz verstehe, ist warum du die void f(); im namespace hast. Sollte ich das auch machen? Ich habe das bisher immer so gemacht, wie du es aus meinem Code ablesen kannst. Oder ist das unfein?

    Ich habe nun das Problem, dass er meint

    error C3861: "aendern": Bezeichner wurde nicht gefunden.

    .

    Kannst du mir diese Schreibweise erläutern: [quote]namespacename::text = std::string(stringname);



    Die globale Variable Inhalt_richTextBox1 im Namespace diese_und_jene_Variable brauchst Du nicht, wenn Du den Text sowiso als Argument der Funktion aendern(..) übergibtst. (Was auch gut so ist.)

    error C3861: "aendern": Bezeichner wurde nicht gefunden.

    Die Fehlermeldung deutet daraufhin, dass vermutlich calc.h in form1.h nicht inkludiert ist. Also: calc.h in form1.h inkludieren. Wenn das schon der Fall ist, ist die Funktion in calc.h nicht deklariert - das geht so:

    calc.h

    #pragma once
    
    namespace diese_und_jene_Variable
    {
       // ...
    }
    std::string aendern(std::string Inhalt_richTextBox1);
    

    Schreibe die Zwischenresultate ruhig in Variablen - so siehst Du welche Typen mit welchen interagieren müssen. Das würde z.B. bei MessageBox::Show(aendern(...)); helfen, denn aendern(..) gibt ein std::string zurück und MessageBox::Show(..) kann mit dem nicht umgehen.

    Was ich an deinem Code nicht ganz verstehe, ist warum du die void f(); im namespace hast. Sollte ich das auch machen? Ich habe das bisher immer so gemacht, wie du es aus meinem Code ablesen kannst. Oder ist das unfein?

    Namespaces sind da um Namenskonflikte zu vermeiden... das sasgt irgendwie schon alles, oder? Unfein oder nicht...? Ich finde Namespaces kann zur logischen Unterteilung benutzt werden und das ist fein. 🕶

    Kannst du mir diese Schreibweise erläutern:

    namespacename::text = std::string(stringname);

    Ich nehme an du sprichst auf folgende Code Zeilen an, siehe anbei meine Kommentare im Code:

    // Text der richTextBox in eine Variable vom Typ String^ (Ref. Handle auf String Objekt) speichern.
    String^ s = richTextBox->Text;
    
    // Inhalt von s (Typ: System::String^) in calc::text (Typ: std::string) konvertieren.
    calc::text = marshal_as<std::string>(s);
    
    // Geänderter std::string mit Hilfe von c_str() wieder in ein System::String umwandeln und bei der richTextBox den Text setzten.
    richTextBox->Text = gcnew String(calc::text.c_str());
    

    Edit:
    Das ist eben das Problem an der Sache. Du musst beide Welten (.NET und native C++) kennen und zwar gut. An den Grenzen musst Du jeweils von der einen Welt in die andere konvertieren. Du musst andauernd Probleme lösen, die Du dir durch den Windows Forms / C++/CLI Ansatz einhandeltst und sonst gar nicht hättest.



  • included hatte ich das schon, deswegen habe ich mich gewundert, wo mein Fehler her kam. Den Rest deiner Antwort muss ich jetzt mal in Ruhe durchgehen 🙂



  • Ich glaube ich komme der Sache näher 🙂

    - ich habe die math.h included in form1.h (hatte ich vorher schon) und diese beiden Zeilen habe ich ebenfalls übernommen:

    #include <msclr\marshal_cppstd.h>
    using namespace msclr::interop;

    - was genau macht marshal_as - ich habe ein wenig nachgegoogelt und gefunden, dass es für native/managed Variablen und dem herumwechseln damit zu tun hat - wann ist mein "uebergabe_string_1" managed, wann ist er native?

    - kann ich generell wenn ich eine funktion habe immer Variablen aus verschiedenen Namespaces verwenden, indem ich

    namespacename::variablenname

    verwende, anstelle von

    using namespacename;

    und dann gilt es für die ganze Funktion? Oder genauer gefragt, unterscheidet der Compiler (korrekt) zwischen

    namespacename1::Variable1

    und

    namepspacename2::Variable1

    ?

    Jetzt bekomme ich beim Ausführen folgenden Fehler:

    1> calc.cpp
    1>v3.obj : error LNK2028: Nicht aufgelöstes Token (0A000021) ""void __clrcall diese_und_jene_Variable::aendern(void)" (?aendern@diese_und_jene_Variable@@$$FYMXXZ)", auf das in Funktion ""private: void __clrcall v3::Form1::mach_was_Click(class System::Object ^,class System::EventArgs ^)" (?mach_was_Click@Form1@v3@@$$FAAAMXPAAMXPAAVObject@System@@PAAVEventArgs@4@@Z)" verwiesen wird. 1>v3.obj : error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""void \_\_clrcall diese\_und\_jene\_Variable::aendern(void)" (?aendern@diese\_und\_jene\_Variable@@FYMXXZ)"inFunktion""private:void__clrcallv3::Form1::mach_was_Click(classSystem::Object,classSystem::EventArgs)"(?mach_was_Click@Form1@v3@@FYMXXZ)" in Funktion ""private: void \_\_clrcall v3::Form1::mach\_was\_Click(class System::Object ^,class System::EventArgs ^)" (?mach\_was\_Click@Form1@v3@@FAFAAAMXPAAVObject@System@@PAAVObject@System@@PAAVEventArgs@4@@Z)".
    1>C:\Users...exe : fatal error LNK1120: 2 nicht aufgelöste externe Verweise.

    Hier meine Dateien:

    calc.cpp

    #include "stdafx.h"
    #include <sstream>
    #include <string>
    #include <cmath>
    #include "calc.h"
    
    using namespace std;
    
    namespace diese_und_jene_Variable	{
    
    	// Anfangswerte festsetzen
    
    	int block_whs = 0;
    	int block_abstand = 0;
    	int zeichen_anz = 0;
    	std::string uebergabe_string_1;
    }
    
    void aendern()	{
    	diese_und_jene_Variable::uebergabe_string_1 = "Test erfolgreich!"; 
    
    };
    

    calc.h:

    #pragma once
    
    #ifndef CALC_H
    #define CALC_H
    
    #include <string>
    
    namespace diese_und_jene_Variable	{
    
    	extern int block_whs;
    	extern int block_abstand;
    	extern int zeichen_anz;
    	extern std::string uebergabe_string_1;
    
    void aendern(); 
    }
    
    #endif
    

    form1.h (auszugsweise):

    #pragma once
    #include "calc.h"
    #include <msclr\marshal_cppstd.h>
    using namespace msclr::interop; 
    namespace v3 {
    
    	using namespace System;
    	using namespace System::IO;
    	using namespace System::ComponentModel;
    	using namespace System::Collections;
    	using namespace System::Windows::Forms;
    	using namespace System::Data;
    	using namespace System::Drawing;
    
    	/// <summary>
    	/// Zusammenfassung für Form1
    	/// </summary>
    .
    .
    .
    .
    
    private: System::Void richTextBox1_TextChanged_1(System::Object^  sender, System::EventArgs^  e) {
    		 }
    private: System::Void mach_was_Click(System::Object^  sender, System::EventArgs^  e) {
    
    			//erstellt einen String vom Typ system::String^ 
    			String^ Inhalt_richTextBox1 = richTextBox1->Text;
    
    			//Konvertiert den String in das Format std::string
    			diese_und_jene_Variable::uebergabe_string_1 = marshal_as<std::string>(Inhalt_richTextBox1); 
    
    			//Funktionsaufruf
    			diese_und_jene_Variable::aendern();
    
    			//Konvertierung von std::string zurück in System::string und Ausgabe in der richTextBox1
    			richTextBox1->Text = gcnew String(diese_und_jene_Variable::uebergabe_string_1.c_str()); 
    
    			 }
    private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e) {
    		 }
    };
    }
    


  • Ich fürchte ich habe deinen Punkt 2 nur bruchstückhaft befolgt *ascheaufmeinhaupt*.

    Wenn ich nun also der Funktion aendern den String aus richTextBox1 übergeben will, muss ich sie mit

    std::String Funktionsname(std::String Stringname1, std::String Stringname2 ...);
    

    aufrufen. Dann muss ich aber in der calc.h sie erst nach dem namespace definieren, in den Namespace müssen Stringname1 und Stringname2 nicht hinein, sehe ich das richtig?

    Noch eine weitere Frage habe ich: Kann man generell sagen, dass alles was ich zum rechnen verwende immer std:: sein muss und alles was aus einer Form kommt immer System:: ist?



  • Ich glaube ich komme der Sache näher

    Da bin ich mir nicht so sicher... 🕶

    Es soll gesagt sein: Benutze cmath anstatt math.h.

    - was genau macht marshal_as - ich habe ein wenig nachgegoogelt und gefunden, dass es für native/managed Variablen und dem herumwechseln damit zu tun hat - wann ist mein "uebergabe_string_1" managed, wann ist er native?

    Genau, es konvertiert zwischen managed (C++/CLI) und native (C++). System::String ist managed, std::string ist native.

    - kann ich generell wenn ich eine funktion habe immer Variablen aus verschiedenen Namespaces verwenden, indem ich...

    Ja. Das gilt nicht nur für Variablen, sondern für Klasse, Structs, Funktionen...

    und dann gilt es für die ganze Funktion? Oder genauer gefragt, unterscheidet der Compiler (korrekt) zwischen...

    Ja. Das sind zwei unterschiedliche Variablen.

    Jetzt bekomme ich beim Ausführen folgenden Fehler: ...

    Nicht beim Ausführen, sondern beim Linken. Das kommt daher, dass Du die Funktion aendern(..) im Namespace diese_und_jene_Variable deklariert, aber im globablen NS definiert hast. Der Linker findet die Definition der Funktion nicht!

    Noch eine weitere Frage habe ich: Kann man generell sagen, dass alles was ich zum rechnen verwende immer std:: sein muss und alles was aus einer Form kommt immer System:: ist?

    Kann man sagen wenn man will. Könnte man so als Daumenregel halten.

    Hier noch eine korrigierte Fassung deines Codes.

    In Form1.h

    private: System::Void mach_was_Click(System::Object^  sender, System::EventArgs^  e) {
    
            std::string text = marshal_as<std::string>(richTextBox1->Text); 
            std::string resultat = diese_und_jene_Variable::aendern(text);
            richTextBox1->Text = gcnew String(resultat.c_str());
        }
    

    calc.h

    #pragma once
    
    #include <string>
    
    namespace diese_und_jene_Variable
    {
        std::string aendern(const std::string& text); 
    }
    

    calc.cpp

    #include "stdafx.h"
    #include "calc.h"
    
    namespace diese_und_jene_Variable
    {
        std::string aendern(const std::string& text)
        {
            return "Test erfolgreich: " + text;
        };
    }
    

    Irgendwie ist das ziemlich hoffnungslos - es fehlen die Grundlagen, aber ganz krass.

    Edit:
    a.) Ich hoffe Du hast bemerkt, dass keine globale Variable (std::string Inhalt_richTextBox1;) nötig ist.
    b.) Das Argument bei der Funktion aendern(..) als const & zu Übergeben erspart eine Kopie (nur als Hinweis).



  • Ja, das bemerke ich auch gerade. Ich dachte mir circa vor ein paar Tagen noch sowas wie "Wie schwer kann es sein, deine ganzen Fortran Codes in Visual umzuschreiben..." 🙂

    Werd mich weiter durchackern, danke für deine Hilfe soweit!



  • Noch eine Frage: Warum cmath statt math.h?



  • Edith: ich hatte eh cmath - oder meintest du es andersrum? Lieber math.h?



  • conqueror24 schrieb:

    Edith: ich hatte eh cmath - oder meintest du es andersrum? Lieber math.h?

    Lieber cmath weil Standard.



  • Kann ich das getrost vergessen (Kein Fehler, ein Warning)?

    Warnung	1	warning C4101: 'ex': Unreferenzierte lokale Variable	c:\users\ra\desktop\vigenere\vigenere\v3\Form1.h	185
    

    Das kommt von der Zeile:

    catch(Exception^ ex)	{MessageBox::Show("Datei unlesbar"); }
    

    Oder gibt es einen guten Grund, warum ich die Warnung nicht einfach übergehen sollte 🙂



  • Noch eine Frage zu dem Punkt von vorher:

    calc.h

    namespace ns1	{
    	std::string irgendeinstring2(const std::string& irgendeinstring1);
    	}
    

    calc.cpp:

    namespace ns1	{ irgendeinevariable = 0;
    	std::string funktionsname(const std::string& Inhalt_rTB1)	{	
    	ns1::irgendeinevariable1 = ...
    return irgendeinevariable1};
    }
    

    und alternativ:

    calc.cpp:

    namespace ns1	{ irgendeinevariable = 0; };
    
    std::string funktionsname(const std::string& Inhalt_rTB1)	{	
    	using namespace ns1;
    	irgendeinevariable1 = 5;
    return irgendeinevariable1};
    

    Ich finde es irgendwie komisch immer in einem Namespace zu arbeiten? Oder soll das so sein und mein Gefühl betrügt mich? Warum muss ich, obwohl ich innerhalb der {} des Namespaces bin ihn dann noch immer mit namespacenamen:: vor die Variablen setzen?



  • conqueror24 schrieb:

    Kann ich das getrost vergessen (Kein Fehler, ein Warning)?

    Warnung	1	warning C4101: 'ex': Unreferenzierte lokale Variable	c:\users\ra\desktop\vigenere\vigenere\v3\Form1.h	185
    

    Das kommt von der Zeile:

    catch(Exception^ ex)	{MessageBox::Show("Datei unlesbar"); }
    

    Ja, kannst Du. Oder noch besser Du behebst sie, z.B. so:

    catch(Exception^)	{MessageBox::Show("Datei unlesbar"); }
    

    Irgendwie scheint mir in deinem Bsp. der Funktionsname irgendeinstring2 schlecht gewählt. Hältst Du irgendeinstring2 für eine Variable? Das ist es nicht, es ist eine Funktion!

    Ich finde es irgendwie komisch immer in einem Namespace zu arbeiten

    Dann lass es. Namespaces sind wie gesagt ein Mechanismus um zu strukturieren und um Nameskonflikte zu vermeiden. In kleinen Programmen spielt das nicht so eine Rolle.

    Warum muss ich, obwohl ich innerhalb der {} des Namespaces bin ihn dann noch immer mit namespacenamen:: vor die Variablen setzen?

    Das musst Du nicht. Wenn doch, machst Du was falsch.

    Lass endlich die globalen Variablen weg - Du brauchst sie nicht! Übergib alles nötige als Parameter und gib falls nötig einen Wert zurück. Die Funktionen sollen möglichst keine Seiteneffekte haben (wie z.B. eine globale Variable ändern).



  • Ja du hast (natürlich) recht, da habe ich mich ein wenig verlesen (oder so halb, weil ich noch einen zweiten Namespace verwendet hatte im ersten). Und ja, das war ein dumm getaufter Funktionsname.

    Du meinst also (dein Beispiel hier nochmal, weil ich es so verwendet habe)

    private: System::Void mach_was_Click(System::Object^  sender, System::EventArgs^  e) {
    
            std::string Inhalt_richTextBox1 = marshal_as<std::string>(richTextBox1->Text);
            std::string resultat = namespacename::Funktionsname(Inhalt_richTextBox1);
            richTextBox1->Text = gcnew String(resultat.c_str()); }
    

    Wenn ich nun aber nicht

    namespacename::Funktionsname(Inhalt_richTextBox1)
    

    mache, wie bekomme ich dann den Inhalt von meiner TextBox dahin, wo ich damit herumrechnen/arbeiten kann? Also sprich in die calc.cpp Datei?



  • conqueror24 schrieb:

    Wenn ich nun aber nicht

    namespacename::Funktionsname(Inhalt_richTextBox1)
    

    mache, wie bekomme ich dann den Inhalt von meiner TextBox dahin, wo ich damit herumrechnen/arbeiten kann? Also sprich in die calc.cpp Datei?

    Auch als Argumente von Funktionen - die argumente können auch weitergereicht werden.



  • Darf ich das dann so machen?

    form1.h

    std::string Inhalt_rTB1 = marshal_as<std::string>(richTextBox1->Text);
    richTextBox2->Text = gcnew String(funktionsname(Inhalt_rTB1).c_str());
    


  • conqueror24 schrieb:

    Darf ich das dann so machen?

    form1.h

    std::string Inhalt_rTB1 = marshal_as<std::string>(richTextBox1->Text);
    richTextBox2->Text = gcnew String(funktionsname(Inhalt_rTB1).c_str());
    

    Ja. 🙂


Anmelden zum Antworten