template fehler... aber warum?



  • Moins,

    Ich hab ein template welches nicht so tut wie es sollte...
    Mach ich was falsch?

    gcc -v

    Using built-in specs.
    Configured with: FreeBSD/i386 system compiler
    Thread model: posix
    gcc version 3.4.6 [FreeBSD] 20060305
    

    code:

    /* header der class */
     template<class T> void operator%(T arg);
    
    /* src der class */
      template<class T> void va_arg::operator%(T arg)
      {
        std::string digit = boost::lexical_cast<std::string>(arg);
        liste.push_back(Glib::locale_to_utf8(digit));
      } /* end function va_arg::operator%(T) */
    
    /* test src */
     test_va % static_cast<omp::uint32>(age);
    

    compiler fehler:

    /var/tmp//cctM8OwM.o(.text+0x38f): In function `main':
    /home/pascal/openmediaplayer/test/omp_va_test.cpp:21: undefined reference to `void omp::va_arg::operator%<unsigned int>(unsigned int)'
    


  • Und wie ist dieser Code auf Quelldateien verteilt? Bei Templates muß der Compiler die Instanziierung und Definition der Funktionen an einem Ort haben -> die Implementation gehört direkt in den Header.



  • "header der class" => include/omp_va.hpp
    "src der class" => src/omp_va.cpp
    "test src" => tests/omp_va_test.cpp

    aus den zwei ersten daten gibt es ein omp_va.o
    welches ich dann in "test src" linke.

    EDIT
    Ich soll das in die header datei reinschreiben?



  • CStoll schrieb:

    -> die Implementation gehört direkt in den Header.



  • Genau wie ich es mir gedacht habe - die Implementation der Methode gehört auch in die "omp_va.hpp" 😉

    Zur Erklärung: Die beiden CPP-Files werden unabhängig voneinander compiliert.
    Wenn der Compiler die "omp_va.cpp" anfasst, sieht er zwar den Quellcode der Funktion, aber er weiß nicht, welche Version(en) später tatsächlich benötigt werden. Und weil er nicht alle Versionen erzeugen kann (erstens reicht der Platz dazu nicht aus und zweitens kennt er noch gar nicht alle Typen, die du dort verwenden willst), legt er nur die Instanziierungen an, die er findet - keine.
    Wenn er dann die "omp_va_test.cpp" übersetzen soll, erfährt er die benötigte Spezialisierung. Aber er findet keine Definition dieser Funktion (weder vor Ort noch in der omp_va.o (siehe oben), also beschwert sich zu recht der Linker.



  • Hi,

    nur am Rande: Ich glaube nicht, dass der operator%() hier eine gute Wahl ist für das, was Du fachlich machen möchtest....

    xGhost schrieb:

    ...

    template<class T> void va_arg::operator%(T arg)
      {
        std::string digit = boost::lexical_cast<std::string>(arg);
        liste.push_back(Glib::locale_to_utf8(digit));
      }
    

    ...

    Es ist meistens keine gute Idee, Operatoren anders zu verwenden, als die meisten Nutzer es intuitiv erwarten. Das sage ich aus leidvoller eigener Erfahrung. Und ich würde unter dem "% mit einem integer" doch eher sowas wie Modulus-Division erwartet.

    Für das, nach was es mir aussieht, wäre wohl (wenn überhaupt) der operator<<() besser geeignet.
    Außerdem würde ich nicht void, sondern ein va_arg& zurückgeben, dann kannst Du den operator geeignet verketten ... wie man es z.B. von streams gewohnt ist.

    Gruß,

    Simon2.



  • Simon2 schrieb:

    Für das, nach was es mir aussieht, wäre wohl (wenn überhaupt) der operator<<() besser geeignet.
    Außerdem würde ich nicht void, sondern ein va_arg& zurückgeben, dann kannst Du den operator geeignet verketten ... wie man es z.B. von streams gewohnt ist.

    Eine gute Idee.
    Ich war wohl zu sehr auf OSS und Xorg Implementation fixiert um das zu bemerken 😡

    Das mit dem Verketten verstehe ich nur so halbwegs.
    Hast du mir ein kleines Beispiel oder nen Link?



  • Hi,

    naja, wie z.B. bei der Ausgabe auf Konsole oder File:

    class MyClass {
       int myInt;
    public:
       MyClass(int i) myInt(i) {}
       virtual ostream& print(ostream& out) const { 
          return out << "MyClass: " << myInt; 
       }
    };
    
    // "freier" Operator ist handlicher (brauch nur einen für alle Abkömmlinge von myClass
    ostream& operator<<(ostream& out, MyClass const& m) { return m.print(out); }
    
    int main() {
       MyClass a(1);
       MyClass b(2);
    
       // auf Konsole ...
       cout << "Hallo " << "xGhost" << 3 << a << "\n";
       // ... oder in File
       ofstream out("file.txt");
       out << "ins File " << "geht  auch die" << b << "\n";
    
       // schreibt sich einfach netter als
       cout << "Hallo ";
       cout << "xGhost";
       cout << 3;
       cout << a;
       cout << "\n";
    

    Das funktioniert, weil der betroffene Stream selbst zurückgegeben wird.

    Ähnlich könntest Du Dein va_arg-Objekt zurückgeben:

    struct va_arg {
       vector<int> v;
       va_arg& operator<<(int arg) { v.push_back(arg); return *this; }
    };
    
    int main() {
       va_arg a;
       a << 1 << 2;
       copy(a.v.begin(), a.v.end(), std::ostream_iterator<int>(cout, " ") );   
       return 0;
    }
    

    Andererseits kommt man dann evtl. mit

    cout << a << 3 << "\n";
    

    ein wenig durcheinander..... (falls man noch den operator<<(ostream&, ...) geeignet überlädt).

    Gruß,

    Simon2.



  • Jo, das war der Teil, der mir klar war.
    Muss ich aber nur va_arg& zurück geben, und Verkettung macht
    es von selbst, oder muss ich sonst noch einen anderen operator überladen?

    // Edit //
    Dein edit gesehen, danke



  • "%" zum "zeugs reinstecken" wird auch z.B. von Boost.Regex verwendet. Ist also nicht GANZ so unüblich. Und keine Verwirrung mit den Streamoperatoren. Von daher... finde ich das nicht SO verkehrt.



  • hustbaer schrieb:

    "%" zum "zeugs reinstecken" wird auch z.B. von Boost.Regex verwendet. Ist also nicht GANZ so unüblich. ...

    OK, mir persönlich war das total unbekannt (hach - es gibt ja noch so Vieles, was ich noch nicht kenne ! 😋 ) und ich bin eher übervorsichtig, was operator overloading angeht....

    Ich hoffe, ich konnte trotzdem ein wenig weiterhelfen.

    Gruß,

    Simon2.


Anmelden zum Antworten