Etwas verwirrt mit Headerdateien und weiterreichen von Variablen/Strings
-
Ich habe etwas Probleme mit dem Fassen von Formen von Visual C++
Ich habe einige Numerik Codes geschrieben für mathematische Berechnungen und will diese nun in eine schönere Form bringen... also habe ich den guten alten C Code hergenommen und wollte den (sehr einfach) in eine Windows Form einbinden.
Was ich nun habe ist (hoffentlich recht einfach dargestellt):
1. RichTextBox, da will ich Buchstabenfolgen eingeben können
2. eine calc.cpp in der will ich mit dem was in der Richtextbox steht hantieren können.
3. Ich will die Ausgabe dann wieder in die RichTextBox zurückübergebenWas ich schon hinbekommen habe, ist Dateien in diese RichTextBox hineinzuladen und dort aufzumachen, editieren und zu speichern. Das würde ich gerne weiter verwenden.
Einfach gesagt habe ich nun also die form1.h, calc.h und calc.cpp
Wie kriege ich nun was dass in der Form eingetippt wird über die calc.h in die calc.cpp und wieder retour in die form1.h zum ausgeben?
Einfaches Konzept: Eingabe in RichTextBox -> Button -> Bearbeitung in der calc.cpp -> Nach Bearbeitung Ausgabe in der RichTextBox
Ich habe die calc.h in die form1.h und die calc.cpp und die main.cpp included.
calc.cpp:
#include "stdafx.h" #include <sstream> #include <string> #include <cmath> #include "calc.h" using namespace std; namespace diese_und_jene_variable { int block_whs= 0; int block_abstand = 0; int zeichen_anz = 0; . . . . }
calc.h:
#ifndef CALC_H #define CALC_H namespace diese_und_jene_variablen{ extern int block_whs; extern int block_abstand; extern int zeichen_anz; . . . . } std::string name; #endif
Jetzt konkret: In der RichTextBox1 ist nun eine Zeichenfolge von (nehmen wir mal an Wörtern). Wie muss nun meine String Definition in der calc.h nun aussehen (ich hab es mit std::string versucht, bin aber gescheitert), damit ich in der calc.cpp zum Bleistift die Funktion str.length drauf anwenden kann?
-
Du mischt C++ und C++/CLI... lass das - es gibt keinen guten Grund dafür ausser Interop.
-
Das ist gut möglich, kannst du mir aber bitte auch sagen wo genau ich das tue und wie ich es besser mache, bzw warum das zu vermeiden ist?
-
Windows Forms ist das Gui Toolkit des Net Framework.
Leider das einzige Gui Toolkit was mit der c++ Expressversion mitgeliefert wird.MFC als Gui Toolkit ist bei den kostenpflichtigen Visual Studios bei, oder du verwendest ein Gui Toolkit eines anderen Herstellers.
Da Intellisense im neuen Visual Studio auch wieder für die CLI verfügbar ist, kann man durchaus Windows Forms mit c++ für kleine Aps verwenden, alles was größer ist nicht. Oder wie schon gesagt wurde für Interop. z.B. MFC Anwendungen mit einem Windows Forms Dialog oder Steuerlement, SQL CLR , CLI für den Zugriff, c++ für die Daten.
-
Wobei die Programmierung der Metro Aps in c++ des kommenden VS sieht nach CLI aus.^^
Aber bei dem einen oder anderen Online Auktionshaus bekommt man ein boxes Visual Studio 2003 oder Vs 6 für wirklich kleines Geld, wenn man auf die Neuerungen wie z.B. Ribbons und co verzichten kann und die Finger von den Managed Extension des VS 2002/2003 läst ein funktionierende Lösung.
-
Dean schrieb:
Wobei die Programmierung der Metro Aps in c++ des kommenden VS sieht nach CLI aus.^^
Sieht danach aus - ists aber nicht.. das ist C++/CX und basiert auf COM, nicht .NET.
-
Ich fühl mich ehrlich gesagt sehr wenig geholfen. Ich bin Mathematiker/Physiker. Ich programmiere in F95 normalerweise, oder in mathematica. Ich will jetzt nicht hergehen und mir irgendwas kaufen (das könnte die UNI dann erst wieder nicht verwenden). Ich würde gerne einfach wissen, wie ich mein Problem lösen kann.
-
Ok, ich versuche mal zu helfen, ich werde aber das Gefühl nicht los, das es sehr schwierig wird.
calc.cpp und die main.cpp included
Source (=cpp) Dateien dürfen nicht inkludiert werden. Nur header Dateien.
Aber zeige doch mal noch main.cpp und Form1.h.
-
Zum problem:
Du musst in der Form1.h einfach die "calc.h" includen. Dann kannst Du in Deinen Event-Handlern auf die Dinge von "calc.h" zugreifen und somit auch Methoden in "calc.cpp" aufrufen, wenn Du sie in "calc.h" deklariert hast.
-
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?
-
Du mußt in der "calc.h" natürlich auch den passenden Header einbinden, damit er std:string kennt:
#ifndef CALC_H #define CALC_H #include <string> // ... extern std::string name; #endif
Und so wie bei den anderen Variablen mußt du dann in der "calc.cpp" eine Definition vornehmen:
std::string name;
Globale Variablen sind aber ein sehr unsauberer Programmierstil.
Und so wie einige schon geschrieben haben: C bzw. C++ und CLI zu mischen ist keine gute Idee (vorallendingen da du anscheindend in beiden nicht firm bist).
-
Du mischst da wie gesagt 2 verschiedene Dinge.
std::string ist c++, system::String ist net Framework
c++ verwendet einen nativ Heap, das net Framewortk mit c++/cli einen Heap mit Garbage Collection. Windows Forms ist Net Framework. Wenn Du mit Windows Forms und C++/CLI arbeiten möchtest müssen system:: Datentypen verwendet werden nicht std::.
http://www.functionx.com/cppcli/index.htm
Wenn Du mit c++ arbeiten möchtest kannst Du mit std:: Datentypen arbeiten ( was nicht so verkehrt ist ), dann machen allerdings Windows Forms keinen Sinn bzw es ist mehr als aufwendig. C++/cli ist genau die Schnittstelle zwischen den beiden "Welten".
Als GUI Toolkit( Grafische Oberflächen) bietet sich da wie gesagt die MFC an, das Visual Studio 2010 Pro gibt es als 180 Tage Testversion kostenfrei zum Download. Oder kostenfrei alternativen wie z.B. Qt, WXWidgets oder andere.
Visual C++ 6 oder 2002/2003 Standart gibt es wie gesagt boxed für richtig kleines Geld bei z.B. Ebay für 20,00 oder 50,00 da ist die MFC bei.
-
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@@$$FAAAVObject@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@@AAMXPAAVEventArgs@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!