[gelöst] C++ programmieren unter Anjuta (Debian 6)



  • Bashar schrieb:

    Nein, du bindest normalerweise keine cpp-Dateien ein. (Wo lernt man sowas?) Du compilierst alle cpp-Dateien getrennt. Inkludieren tust du nur Headerdateien.

    Wobei ich in diesem Kontext gleich noch eine Frage nachschieben möchte:

    Ich habe in einem anderen Thread geschrieben, dass ich jetzt versuche mit der GSL zu arbeiten [1].

    Wenn man nun aber einen Blick in das Handbuch, genauer auf das Beispiel wirft, so sieht man, dass dort in eine Datei sowohl header- als auch c-Dateien includiert werden:

    #include <stdio.h>
         #include <gsl/gsl_errno.h>
         #include <gsl/gsl_math.h>
         #include <gsl/gsl_roots.h>
    
         #include "demo_fn.h"
         #include "demo_fn.c"
    
         int
         main (void)
         {
           int status;
           int iter = 0, max_iter = 100;
           const gsl_root_fsolver_type *T;
           gsl_root_fsolver *s;
           double r = 0, r_expected = sqrt (5.0);
           double x_lo = 0.0, x_hi = 5.0;
           gsl_function F;
           struct quadratic_params params = {1.0, 0.0, -5.0};
    
    // und so weiter
    

    Wie ist das zu verstehen?

    Gruß,
    Klaus.

    [1] Daraus ist ja auch erst die Problematik mit dem Compiler und Linker entstanden.



  • Klaus82 schrieb:

    Hi Ohjemene,

    Ohjemene schrieb:

    Nimm Geany:
    http://www.geany.org/

    Und warum?

    Du hattest dich vorher gegen Anjuta ausgesprochen

    Doch warum jetzt für Geany?

    Weil Geany ein zum Progammieren guter Editor ist, deutlich besser als Gedit.
    Anjuta ist eine IDE, eine IDE mit einem Editor zu vergleichen ist wie Apfel und Birnen.

    Und ich halte es für den Anfang nicht für falsch, erstmal nur einen Editor zu benutzen. Wenn man dies tut, dann sollte man allerdings einen guten Editor nehmen. Geany ist so einer.

    Wie gesagt, bisher habe ich viele Programmteile auch ausgelagert:

    #include<iostream>
    #include<cmath>
    #include<fstream>
    // Numerical Recipes
    #include</home/klaus/NumericalRecipes/code/nr3.h>
    #include</home/klaus/NumericalRecipes/code/ran.h>
    #include</home/klaus/NumericalRecipes/code/roots.h>
    // personal header files
    #include</home/klaus/Water/HeaderFiles/F1.h>
    #include</home/klaus/Water/HeaderFiles/F2.h>
    #include</home/klaus/Water/HeaderFiles/physicalConstants.h>
    #include</home/klaus/Water/HeaderFiles/alpha.h>
    #include</home/klaus/Water/HeaderFiles/beta.h>
    

    Gewöhne dir ab, in deinem Quellcode absolute Pfade zu verwenden.
    Außerdem gehören eigene Headerdateien in Anführungszeichen gesetzt.
    Spitze Klammern sind nur für Headerdateien gedacht, die Systemweit z.B. unter /usr/include installiert sind.
    In der Regel benutzt man diese für Bibliotheken.

    Ich habe in einem anderen Thread geschrieben, dass ich jetzt versuche mit der GSL zu arbeiten [1].

    Bei weitergabe deines Programms mußt du dann auch den Quellcode unter die GPL stellen und mitliefern, ich hoffe du weißt das.
    Die GSL steht nämlich nicht unter der LGPL, sondern unter der GPL.



  • Alles klar,

    aber jetzt bitte nochmal zurück zu meiner Fragestellung.

    Ich verstehe das Argument von Bashar sehr gut, der da schreibt:

    Bashar schrieb:

    Nein, du bindest normalerweise keine cpp-Dateien ein. (Wo lernt man sowas?) Du compilierst alle cpp-Dateien getrennt. Inkludieren tust du nur Headerdateien.

    Das läuft auf der Kommandozeile zu Fuß folgendermaßen. Zuerst getrennte Compilierung aller cpp-Dateien:

    g++ -Wall -ansi -c foo.cpp
    g++ -Wall -ansi -c bar.cpp
    ...
    

    Es entstehen lauter gleichnamige .o-Dateien. In einem zweiten Schritt werden die nun zusammengelinkt:

    g++ -o meinProgramm foo.o bar.o ...
    

    Was mich jetzt nur ein wenig wundert ist die Fragestellung, dass die zuvor einzeln kompilierten Dateien doch durchaus miteinander verflochten sind. Also mit der obigen Bezeichnung kann es ja sein, dass ich etwas in foo.cpp kompiliere, was ich dann in bar.cpp verwende.

    Aus diesem Grund stelle ich mir gerade naiv vor, dass doch beim kompilieren von bar.cpp ein Fehler entstehen müsste.

    Natürlich macht es intuitiv Sinn, dass ich dann einen Linker aufrufe, der die Dateien anschließend verknüpft. Doch das findet ja nach dem kompilieren statt. Und so beißt sich für mich die Katze irgendwie in den Schwanz. 😕

    Ich kenne es bisher nur von LaTex, dass ich einzelne Kapitel bearbeite und so nicht immer das ganze Dokument kompilieren muss. Doch dann bleiben Querverweise erstmal leer oder werden mit einem ? gefüllt.
    D.h. ich kann Kapitel gar nicht einzeln kompilieren, wenn sie anschließend zu einem Gesamtdokument zusammengefügt werden sollen.

    Wie passt das denn zusammen?

    Gruß,
    Klaus.



  • Es gibt eben die "One Definition Rule". Du darfst für jedes Symbol (Variable, Funktion, Klasse, Struktur, etc.) nur eine Definition (also Implementierung haben) aber beliebig viele Deklarationen. Der Linker löst dann am Ende ohne Probleme die Symbole (LaTeX mäßig eben die ?? bei den Verweisen) auf.

    Bsp

    // foo.hpp
    #ifndef FOO_HPP
    #define FOO_HPP // Include guards um Mehrfacheinbindung zu verhindern
    
    extern void foo(); // Deklaration
    
    #endif
    
    // foo.cpp
    #include "foo.hpp" // Beachte: Keine absoluten Pfade! Niemals nie!
    #include <iostream>
    
    void foo() { // Definition
      std::cout << "Hello World\n";
    }
    
    #include "foo.hpp"
    
    int main() {
      foo(); // Der Compiler generiert hier sozusagen "??" und der Linker löst das auf in einen Aufruf von foo
    }
    

    g++ -Wall -Wextra -pedantic -ansi -c foo.cpp
    g++ -Wall -Wextra -pedantic -ansi -c main.cpp
    g++ foo.o main.o -o foo
    ./foo
    Hello World



  • Ich denke so langsam verstehe ich.

    rüdiger schrieb:

    #include "foo.hpp"
    
    int main() {
      foo(); // Der Compiler generiert hier sozusagen "??" und der Linker löst das auf in einen Aufruf von foo
    }
    

    Was mir hier erst auffällt, dass du nur header Dateien verwendest!

    Das heißt, wenn ich in einem cpp Code eine Variable oder Methode verwenden möchte, dass du muss ich die entsprechende header Datei included sein, in der die Deklaration steht.
    Die Deklaration alleine reicht dem Compiler allerdings noch nicht, da erst die Definition die Information liefert was die Methode auch tut und somit den Compiler zufriedenstellt.

    Ich habe nochmal ein Minimalbeispiel erstellt.

    // linear_function.h
    double linear_function(double x);
    

    und

    // linear_function.cpp
    #include "linear_function.h"
    
    double linear_function(double x)
    {
    double m = 2;
    double b = -2;
       return m*x + b;
    }
    

    Das ganze wird nun hier verwendet

    // composition_function.h
    double composition_function(double x);
    

    Und schließlich die Definition, wobei ich nur die header Datei verwende!

    // composition_function.cpp
    #include "composition_function.h"
    #include "linear_function.h"
    
    double composition_function(double x)
    {
       return x * linear_function(x);
    }
    

    Jetzt bin ich aber doch ein wenig verwirrt. Ich kompiliere zunächst die compositin_functin.cpp

    g++ -Wall -pedantic -ansi -c composition_functin.cpp
    

    Und bekomme keine Fehlermeldung! Wieso das denn? Ist es die Option -c, welche darauf hinweist, dass noch nicht gelinkt werden soll, sprich noch keine Gedanken über Abhängigkeit gemacht werden sollen?
    Der Kompiler weiß durch include "linear_function.h", dass die verwendete Referenz zwar deklariert ist, interessiert sich aber noch nicht für deren Definition?

    Ich habe include "linear_function.h" mal herausgenommen mittels //, dann meckert er

    :~/Cpp/Minimalbeispiele$ g++ -Wall -pedantic -ansi -c composition_function.cpp
    composition_function.cpp: In function ‘double composition_function(double)’:
    composition_function.cpp:7: error: ‘linear_function’ was not declared in this scope
    

    Aha! Sehr interessant. Also schnell wieder hinein.

    Was mich jetzt noch wundert, warum ich nicht linear_function.o und composition_function.o linken kann?

    :~/Cpp/Minimalbeispiele$ g++ linear_function.o composition_function.o -o composition_function
    /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crt1.o: In function `_start':
    (.text+0x20): undefined reference to `main'
    collect2: ld returned 1 exit status
    

    Ich nehem an, es liegt daran, dass in C/C++ alles in einer int main(){} Umgebung liegen muss? Ist das einfach Konvention? Also schnell noch eine main file geschrieben:

    #include <iostream>
    
    #include "linear_function.h"
    #include "composition_function.h"
    
    using namespace std;
    
    int main()
    {
       double value = 3;
    
       cout << "Result = " << composition_function(value) << endl;
    
       return 0;
    }
    

    Kompiliert und dann funktioniert auch das linken wieder und ich bekomme ein Ergebnis:

    :~/Cpp/Minimalbeispiele$ g++ -Wall -pedantic -ansi -c main.cpp
    :~/Cpp/Minimalbeispiele$ g++ main.o linear_function.o composition_function.o -o main
    :~/Cpp/Minimalbeispiele$ ./main
    Result = 12
    :~/Cpp/Minimalbeispiele$
    

    Spielt die Abhängigkeit beim Linken eigentlich eine Rolle, oder schafft der Kompiler das von alleine?

    Bin ich langsam vom Verständnis her auf dem richtigen Weg? 🙂

    Viele Grüße und danke für eure Hilfe, 🙂
    Klaus.



  • Das heißt, wenn ich in einem cpp Code eine Variable oder Methode verwenden möchte, dass du muss ich die entsprechende header Datei included sein, in der die Deklaration steht.
    Die Deklaration alleine reicht dem Compiler allerdings noch nicht, da erst die Definition die Information liefert was die Methode auch tut und somit den Compiler zufriedenstellt.

    Die Deklaration muss nicht in einem Header stehen

    // bar.cpp
    #include <iostream>
    
    void foo() {
      std::cout << "Hallo Welt\n";
    }
    
    // foo.cpp
    extern void foo();
    
    int main() {
      foo();
    }
    

    geht genauso. Und der Compiler braucht nur die Deklaration. Erst der Linker muss dann wissen wo er die Definition finden kann, damit der den Aufruf von foo an die richtige Stelle setzt.

    Ist es die Option -c, welche darauf hinweist, dass noch nicht gelinkt werden soll, sprich noch keine Gedanken über Abhängigkeit gemacht werden sollen?

    Ja

    Was mich jetzt noch wundert, warum ich nicht linear_function.o und composition_function.o linken kann?

    Ein ausführbares Programm braucht eine main-Funktion.



  • Hi,

    rüdiger schrieb:

    Und der Compiler braucht nur die Deklaration. Erst der Linker muss dann wissen wo er die Definition finden kann, damit der den Aufruf von foo an die richtige Stelle setzt.

    Alles klar.

    Aber was mich nach wie vor wundert: Cpp Dateien einbinden oder nicht?

    Ich bin immernoch irritiert von Bashars Aussage, der da schreibt:

    Nein, du bindest normalerweise keine cpp-Dateien ein. (Wo lernt man sowas?) Du compilierst alle cpp-Dateien getrennt. Inkludieren tust du nur Headerdateien.

    Aber bei dem Thread zum Minimalbeispiel der GSL binden wir cpp Dateien ein und es hat niemanden gestört.

    Ich kann das Beispiel auch mal 'rüberholen':

    Klaus82 schrieb:

    rng.h

    double get_random_number();
    

    Dann das ganze in eine cpp-Datei eingebunden:
    rng.cpp

    double get_random_number() 
    {
    const gsl_rng_type* T;
    gsl_rng* r;
    gsl_rng_env_setup();
    
    T = gsl_rng_default;
    r = gsl_rng_alloc (T);
    
    return gsl_rng_uniform (r);
    
    gsl_rng_free (r);
    }
    

    Schließlich habe ich das ganze in ein Hauptprogramm eingebunden, worin ich schließlich eine Zufallszahl verwenden möchte:
    main.cpp

    #include<iostream>
    #include<stdio.h>
    // GSL
    #include</usr/local/include/gsl/gsl_rng.h>
    // personal header files
    #include "rng.h"
    #include "rng.cpp"
    
    using namespace std;
    
    int main(){//
    
    cout << "Random number in [0,1[ : " << get_random_number() << endl;
    
    return 0;
    }
    

    Gruß,
    Klaus.



  • Klaus82 schrieb:

    Aber was mich nach wie vor wundert: Cpp Dateien einbinden oder nicht?

    Die Frage hab ich dir schon beantwortet.

    Ich bin immernoch irritiert von Bashars Aussage, der da schreibt:

    Nein, du bindest normalerweise keine cpp-Dateien ein. (Wo lernt man sowas?) Du compilierst alle cpp-Dateien getrennt. Inkludieren tust du nur Headerdateien.

    Achso, du wartest darauf, dass jemand dir die Antwort gibt, die du hören willst.

    Aber bei dem Thread zum Minimalbeispiel der GSL binden wir cpp Dateien ein und es hat niemanden gestört.

    rüdiger dürfte das einfach übersehen haben, wofür ja auch seine Antwort, "Du hast vergessen rng.cpp zu kompilieren und zu linken!" spricht. Sonst hat sich dazu keiner geäußert.



  • Hi,

    Bashar schrieb:

    Achso, du wartest darauf, dass jemand dir die Antwort gibt, die du hören willst.

    😕

    Bashar schrieb:

    Aber bei dem Thread zum Minimalbeispiel der GSL binden wir cpp Dateien ein und es hat niemanden gestört.

    rüdiger dürfte das einfach übersehen haben, wofür ja auch seine Antwort, "Du hast vergessen rng.cpp zu kompilieren und zu linken!" spricht.

    Okay.

    Bashar schrieb:

    Sonst hat sich dazu keiner geäußert.

    Das ist ja nach wie vor der Punkte an dem ich knabbere. Rüdigers Aussage scheint zu bedeuten, dass es ein no-go ist cpp-Dateien einzubinden. In dem GSL Beispiel für Zufallszahlen wird es aber getan.

    Das lässt für mich den Schluß zu, dass es prinzipiell möglich ist nur scheinbar schlecher Programmierstil?

    Ich möchte es einfach einordnen können. 🙂

    Gruß,
    Klaus.



  • #include macht einfach nur eine Textersetzung. Das ist keine Magie hinter. Du kannst also jede Datei einbinden, die du einbinden willst. Auch die Trennung zwischen Header- und Codedatei ist rein willkürlich. Nur hält man sich normalerweise daran, dass man nur Header einbindet und in Header nur Deklarationen (ggf. inline Funktionen) schreibt. Bei dem Thema habe ich nur übersehen, dass du eine Codedatei eingebunden hast, sonst hätte ich dich darauf aufmerksam gemacht. Einbinden von Codedateien (also Definitionen) ist gefährlich, da man so schnell die "One Definition Rule" (ODR) verletzt. Außerdem ist ein großer Vorteil beim aufteilen in verschiedene Codedateien, dass der Compiler nicht jedes mal das ganze Projekt neu kompilieren muss und der Compilevorgang wesentlich schneller abläuft. Dies trifft jedoch nur zu, wenn man keine Codedateien inkludiert und die Header möglichst klein sind (immer nur das inkludieren was man braucht!)



  • Hi,

    rüdiger schrieb:

    Einbinden von Codedateien (also Definitionen) ist gefährlich, da man so schnell die "One Definition Rule" (ODR) verletzt.

    Okay, danke. Das klingt natürlich sehr einleuchtend. 🙂

    Also merken: In die Hauptdatei des Programms, wo int main(){} steht, nur header Dateien NAME.h includieren.

    Die zugehörigen cpp Dateien separat kompilieren.

    Wobei ich gerade mit den Abhängigkeiten ein wenig kämpfe, doch dazu mache ich mal einen neuen Thread auf.

    Gruß,
    Klaus.



  • rüdiger schrieb:

    #include "foo.hpp"
    
    int main() {
      foo(); // Der Compiler generiert hier sozusagen "??" und der Linker löst das auf in einen Aufruf von foo
    }
    

    Das ist falsch!

    Der Compiler kennt foo() als Deklaration, weil es ihm über die includierte Headerdatei bekannt gemacht wurde.
    Er weiß also den Funktionsnamen etwaige Funktionsparameter und den Rückgabetyp der Funktion und so wird das in die Objektdatei von main.o auch eingebaut.

    Der Linker führt dann nur noch main.o und foo.o zusammen.
    foo.o enthält dabei auch die Implementierung der Deklaration foo().



  • Klaus82 schrieb:

    Also merken: In die Hauptdatei des Programms, wo int main(){} steht, nur header Dateien NAME.h includieren.

    Falsch!
    Nicht nur in der Hauptdatei sondern in allen *.cpp Quellcodedateien nur header Dateien *.h includieren!

    Die zugehörigen cpp Dateien separat kompilieren.

    Makefiles benutzen!


Anmelden zum Antworten