Προς το περιεχόμενο

Function prototype matching in C++


gon1332

Προτεινόμενες αναρτήσεις

Ορίστε ένα πρόγραμμα με κλάσεις για εκπαιδευτικούς σκοπούς C++:

 

screen.hh

 

 

#ifndef SCREEN_HH_C6XEKWVZ
#define SCREEN_HH_C6XEKWVZ

#include <string>
using std::string;

class Screen {
public:
        //=== Hide the details of how Screen is implemented.
        typedef string::size_type pos;
        // Can also use this:
        // using pos = string::size_type;

        //=== Constructors
        Screen(void) = default;
        Screen(pos, pos, char);

        //=== Function members
        char    get (void)     const;
        char    get (pos, pos) const;
        Screen &set (char);
        Screen &set (pos, pos, char);
        Screen &move(pos, pos);
        void some_member(void) const;

private:
        mutable size_t access_ctr;   // may change even in a const object
        pos cursor = 0;
        pos height = 0,
            width  = 0;
        string contents;
};

#endif /* SCREEN_HH_C6XEKWVZ */
 

 

 

 

screen.cpp

 

 

#include "screen.hh"

// Constructors definitions
Screen::Screen(pos ht, pos wd, char c):
        height(ht), width(wd), contents(ht * wd, c) { }

// Member functions definitions

///////////////////////////////////////////////////////////////////////////////
// Move to (r,c) position.
///////////////////////////////////////////////////////////////////////////////
inline
Screen &Screen::move(pos r, pos c)
{
        pos row = r * width;
        cursor = row + c;
        return *this;
}

///////////////////////////////////////////////////////////////////////////////
// Set position under cursor to character c.
///////////////////////////////////////////////////////////////////////////////
inline
Screen &Screen::set(char c)
{
        contents[cursor] = c;
        return *this;
}

///////////////////////////////////////////////////////////////////////////////
// Set position (r, c) to character c.
///////////////////////////////////////////////////////////////////////////////
inline
Screen &Screen::set(pos r, pos c, char ch)
{
        contents[r * width + c] = ch;
        return *this;
}

///////////////////////////////////////////////////////////////////////////////
// Get contents of position under cursor.
///////////////////////////////////////////////////////////////////////////////
inline
char Screen::get(void) const
{
        return contents[cursor];
}

///////////////////////////////////////////////////////////////////////////////
// Get contents of position (r, c).
///////////////////////////////////////////////////////////////////////////////
inline
char Screen::get(pos r, pos c) const
{
        pos row = r * width;
        return contents[row + c];
}

inline
void Screen::some_member() const
{
        ++access_ctr;   // keep a count of the calls to any member function
        // whatever other work this member needs to do
}
 

 

 

 

main.cpp

 

 

#include <iostream>
#include "screen.hh"

int main(void)
{
        Screen myscreen;
        char ch = myscreen.get(); // calls Screen::get()
        std::cout << ch << std::endl;
        ch = myscreen.get(0, 0);
        std::cout << ch << std::endl;

        myscreen.move(4, 0).set('#');
        return 0;
}
 

 

 

 

g++ output

 

 

$ g++ -std=c++11 -Wall -Wextra *.hh *.cpp

/tmp/ccQi0vG8.o: In function `main':
main.cpp:(.text+0x1d): undefined reference to `Screen::get() const'
main.cpp:(.text+0x53): undefined reference to `Screen::get(unsigned long, unsigned long) const'
main.cpp:(.text+0x89): undefined reference to `Screen::move(unsigned long, unsigned long)'
main.cpp:(.text+0x96): undefined reference to `Screen::set(char)'
collect2: error: ld returned 1 exit status 

 

 

 

Δε μπορώ να καταλάβω γιατί δεν γίνεται σωστά το linking. Παίζει να είναι κάτι προφανές...

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Με optimizations off το ίδιο; Δε βλέπω κάτι προφανές αλλά αν χωρίς optimizations δουλεύει τότε κάποιο ψυχολογικό έχει με το inline.

Στον gcc είναι default να μην κάνει optimizations (-Ο0). Το έβαλα κι εγώ για σιγουριά, αλλά τα ίδια.

 

Έβαλα σε σχόλια όλα τα inline και μου δούλεψε. Κανένα warning.

 

Υπάρχει κάποιο θέμα με τα inlines;

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Αυτό που καταλαβαίνω πως γίνεται είναι ότι ο compiler κάνει inline τις functions (<rant>σταματήστε τα premature optimization</rant>) (πράγμα που δε μπορώ να φανταστώ ότι γίνεται σε -O0, wtf?!?!? αλλά πάλι δε νοιώθω πολύ από g++) αλλά παρόλα αυτά μέσα στο main.o βάζει αναφορές για τις inlined συναρτήσεις μέσα στο symbol table.

 

O ld από την πλευρά του βλέπει τις αναφορές, βλέπει ότι κανένα από τα object αρχεία που έχει να δουλέψει δεν παρέχει ορισμό για να κάνει resolve αυτές τις αναφορές και νίπτει τας χείρας του (καλά κάνει, δε φταίει ο ld).

 

Τώρα το γιατί ο g++ κάνει inline αλλά δεν καταλαβαίνει πως πρέπει να μην αφήνει references για τις συναρτήσεις που έχουν γίνει 100% inlined... δεν ξέρω.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Αυτό που καταλαβαίνω πως γίνεται είναι ότι ο compiler κάνει inline τις functions (<rant>σταματήστε τα premature optimization</rant>) (πράγμα που δε μπορώ να φανταστώ ότι γίνεται σε -O0, wtf?!?!? αλλά πάλι δε νοιώθω πολύ από g++) αλλά παρόλα αυτά μέσα στο main.o βάζει αναφορές για τις inlined συναρτήσεις μέσα στο symbol table.

 

O ld από την πλευρά του βλέπει τις αναφορές, βλέπει ότι κανένα από τα object αρχεία που έχει να δουλέψει δεν παρέχει ορισμό για να κάνει resolve αυτές τις αναφορές και νίπτει τας χείρας του (καλά κάνει, δε φταίει ο ld).

 

Τώρα το γιατί ο g++ κάνει inline αλλά δεν καταλαβαίνει πως πρέπει να μην αφήνει references για τις συναρτήσεις που έχουν γίνει 100% inlined... δεν ξέρω.

Δοκίμασα και το άλλο. Χωρίς το keyword inline, έκανα compile με -O3. Όλα είναι καλά. Μόνο όταν βάζω κάποιο inline keyword συμβαίνει αυτό που λες λογικά. Πάντως όντως φαίνεται πολύ ηλίθιο αν συμβαίνει ό,τι λες. Σίγουρα θα υπάρχει κάποια εξήγηση.

 

Δοκίμασα να το τρέξω και με τον clang++, αλλά τα ίδια (με inline για την get):

 

 

$ clang++ -v -std=c++11 -O3 -Wall -Wextra  *.cpp

Debian clang version 3.0-6.2 (tags/RELEASE_30/final) (based on LLVM 3.0)
Target: x86_64-pc-linux-gnu
Thread model: posix
 "/usr/bin/clang" -cc1 -triple x86_64-pc-linux-gnu -emit-obj -disable-free -disable-llvm-verifier -main-file-name main.cpp -mrelocation-model static -mdisable-fp-elim -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-linker-version 2.22 -momit-leaf-frame-pointer -v -resource-dir /usr/bin/../lib/clang/3.0 -fmodule-cache-path /var/tmp/clang-module-cache -internal-isystem /usr/include/c++/4.6 -internal-isystem /usr/include/c++/4.6/x86_64-linux-gnu -internal-isystem /usr/include/c++/4.6/backward -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/clang/3.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /usr/include -O3 -Wall -Wextra -std=c++11 -fdeprecated-macro -ferror-limit 19 -fmessage-length 150 -fgnu-runtime -fobjc-runtime-has-arc -fobjc-runtime-has-weak -fobjc-fragile-abi -fcxx-exceptions -fexceptions -fdiagnostics-show-option -fcolor-diagnostics -o /tmp/main-650rJc.o -x c++ main.cpp
clang -cc1 version 3.0 based upon llvm 3.0 hosted on x86_64-pc-linux-gnu
ignoring nonexistent directory "/usr/bin/../lib/clang/3.0/include"
ignoring nonexistent directory "/usr/include/c++/4.6//x86_64-linux-gnu/64"
ignoring nonexistent directory "/usr/bin/../lib/clang/3.0/include"
ignoring duplicate directory "/usr/include/c++/4.6"
ignoring duplicate directory "/usr/include/c++/4.6/backward"
ignoring duplicate directory "/usr/include/c++/4.6"
ignoring duplicate directory "/usr/include/c++/4.6/x86_64-linux-gnu"
ignoring duplicate directory "/usr/include/c++/4.6/backward"
ignoring duplicate directory "/usr/include/c++/4.6"
ignoring duplicate directory "/usr/include/c++/4.6/x86_64-linux-gnu"
ignoring duplicate directory "/usr/include/c++/4.6/backward"
ignoring duplicate directory "/usr/local/include"
ignoring duplicate directory "/usr/include/x86_64-linux-gnu"
ignoring duplicate directory "/usr/include/x86_64-linux-gnu"
ignoring duplicate directory "/usr/include/x86_64-linux-gnu"
ignoring duplicate directory "/usr/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/include/c++/4.6
 /usr/include/c++/4.6/x86_64-linux-gnu
 /usr/include/c++/4.6/backward
 /usr/local/include
 /usr/include/x86_64-linux-gnu
 /usr/include
 /usr/include/clang/3.0/include/
 /usr/lib/gcc/x86_64-linux-gnu/4.6/include/
 /usr/lib/gcc/x86_64-linux-gnu/4.6/include-fixed/
End of search list.
 "/usr/bin/clang" -cc1 -triple x86_64-pc-linux-gnu -emit-obj -disable-free -disable-llvm-verifier -main-file-name screen.cpp -mrelocation-model static -mdisable-fp-elim -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-linker-version 2.22 -momit-leaf-frame-pointer -v -resource-dir /usr/bin/../lib/clang/3.0 -fmodule-cache-path /var/tmp/clang-module-cache -internal-isystem /usr/include/c++/4.6 -internal-isystem /usr/include/c++/4.6/x86_64-linux-gnu -internal-isystem /usr/include/c++/4.6/backward -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/clang/3.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /usr/include -O3 -Wall -Wextra -std=c++11 -fdeprecated-macro -ferror-limit 19 -fmessage-length 150 -fgnu-runtime -fobjc-runtime-has-arc -fobjc-runtime-has-weak -fobjc-fragile-abi -fcxx-exceptions -fexceptions -fdiagnostics-show-option -fcolor-diagnostics -o /tmp/screen-1LkFCT.o -x c++ screen.cpp
clang -cc1 version 3.0 based upon llvm 3.0 hosted on x86_64-pc-linux-gnu
ignoring nonexistent directory "/usr/bin/../lib/clang/3.0/include"
ignoring nonexistent directory "/usr/include/c++/4.6//x86_64-linux-gnu/64"
ignoring nonexistent directory "/usr/bin/../lib/clang/3.0/include"
ignoring duplicate directory "/usr/include/c++/4.6"
ignoring duplicate directory "/usr/include/c++/4.6/backward"
ignoring duplicate directory "/usr/include/c++/4.6"
ignoring duplicate directory "/usr/include/c++/4.6/x86_64-linux-gnu"
ignoring duplicate directory "/usr/include/c++/4.6/backward"
ignoring duplicate directory "/usr/include/c++/4.6"
ignoring duplicate directory "/usr/include/c++/4.6/x86_64-linux-gnu"
ignoring duplicate directory "/usr/include/c++/4.6/backward"
ignoring duplicate directory "/usr/local/include"
ignoring duplicate directory "/usr/include/x86_64-linux-gnu"
ignoring duplicate directory "/usr/include/x86_64-linux-gnu"
ignoring duplicate directory "/usr/include/x86_64-linux-gnu"
ignoring duplicate directory "/usr/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/include/c++/4.6
 /usr/include/c++/4.6/x86_64-linux-gnu
 /usr/include/c++/4.6/backward
 /usr/local/include
 /usr/include/x86_64-linux-gnu
 /usr/include
 /usr/include/clang/3.0/include/
 /usr/lib/gcc/x86_64-linux-gnu/4.6/include/
 /usr/lib/gcc/x86_64-linux-gnu/4.6/include-fixed/
End of search list.
 "/usr/bin/ld" --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a.out /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.6/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.6 -L/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../../lib64 -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.6/../../.. -L/lib/x86_64-linux-gnu -L/lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib /tmp/main-650rJc.o /tmp/screen-1LkFCT.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/4.6/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crtn.o
/tmp/main-650rJc.o: In function `main':
main.cpp:(.text+0xa6): undefined reference to `Screen::get(unsigned long, unsigned long) const'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

 

 

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Το ίδιο πρόβλημα υπάρχει και στην C με το σκέτο inline (εκεί μάλιστα, όπως είχαμε περιγράψει σε παλαιότερο μήνυμα, ο gcc έχει διαφορετική συμπεριφορά αν επιλέξουμε gnu διάλεκτο ή std διάλεκτο).

 

Στο draft του προτύπου της C++ βλέπω να λέει:

 

3.2 One definition rule

 

3 An inline function shall be defined in every translation unit in which it is used.

Το main.cpp είναι ένα "translation unit" στο οποίο δεν έχει οριστεί η συνάρτηση οπότε ο linker βλέπει τη κλήση μιας συνάρτησης για την οποία δεν γνωρίζει τίποτα. Μία λύση που μου έρχεται στο μυαλό είναι να ορίζεις την συνάρτηση μέσα στο header file ώστε να φαίνεται παντού αλλά δεν γνωρίζω C++ οπότε μπορεί να υπάρχει κάποια πολύ καλύτερη (ή φυσικά βγάζεις την χαζομάρα inline)

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Το ίδιο πρόβλημα υπάρχει και στην C με το σκέτο inline (εκεί μάλιστα, όπως είχαμε περιγράψει σε παλαιότερο μήνυμα, ο gcc έχει διαφορετική συμπεριφορά αν επιλέξουμε gnu διάλεκτο ή std διάλεκτο).

 

Στο draft του προτύπου της C++ βλέπω να λέει:

 

 

Το main.cpp είναι ένα "translation unit" στο οποίο δεν έχει οριστεί η συνάρτηση οπότε ο linker βλέπει τη κλήση μιας συνάρτησης για την οποία δεν γνωρίζει τίποτα. Μία λύση που μου έρχεται στο μυαλό είναι να ορίζεις την συνάρτηση μέσα στο header file ώστε να φαίνεται παντού αλλά δεν γνωρίζω C++ οπότε μπορεί να υπάρχει κάποια πολύ καλύτερη (ή φυσικά βγάζεις την χαζομάρα inline)

 

Sry αλλα πως θα κανεις link σε inline function (δηλαδη σε μη function) ;

 

Έβαλα τα definitions στο header file. Δούλεψε με τα keywords! Η αλήθεια είναι πως στο βιβλίο που διαβάζω δεν ανέφερε πως ΠΡΕΠΕΙ να βρίσκονται στο ίδιο αρχείο για να μπορώ να αξιοποιήσω το inlining. Άφησε το context να εννοηθεί πως βρίσκονται στο ίδιο αρχείο, αλλά εγώ είπα να διαχωρίσω τα declarations από τα definitions για πιο ξεκάθαρα source files κι έτσι προέκυψαν τα παράπονα. Δε μου έχει τύχει κάτι τέτοιο σε C, γιατί δε βρίσκω λόγο χρήσης του inline keyword. Απλώς το βιβλίο C++ αφιέρωσε λίγο χώρο και παραδείγματα για αυτό, οπότε θεώρησα πως είναι σημαντικό. Απλά μάλλον καλό είναι να γνωρίζουμε πως η C++ προσπαθεί να κάνει inlining για definitions μικρών μεθόδων στο ίδιο translation unit.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Το λαθος σου ειναι λογικο.

 

Μαρκαρεις μια function inline. Δηλαδη λες στον compiler, αυτη εδω την function μην την κανεις compile. Και μετα ζητας απο τον linker να σου βρει μια function που εσυ ειπες να μην γινει compile.

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Το λαθος σου ειναι λογικο.

 

Μαρκαρεις μια function inline. Δηλαδη λες στον compiler, αυτη εδω την function μην την κανεις compile. Και μετα ζητας απο τον linker να σου βρει μια function που εσυ ειπες να μην γινει compile.

Συμφωνώ. Δε μου πέρασε από το μυαλό.

 

Οπότε επιτέλους καταλήξαμε κάπου! :)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Έβαλα τα definitions στο header file. Δούλεψε με τα keywords! Η αλήθεια είναι πως στο βιβλίο που διαβάζω δεν ανέφερε πως ΠΡΕΠΕΙ να βρίσκονται στο ίδιο αρχείο για να μπορώ να αξιοποιήσω το inlining. Άφησε το context να εννοηθεί πως βρίσκονται στο ίδιο αρχείο, αλλά εγώ είπα να διαχωρίσω τα declarations από τα definitions για πιο ξεκάθαρα source files κι έτσι προέκυψαν τα παράπονα. Δε μου έχει τύχει κάτι τέτοιο σε C, γιατί δε βρίσκω λόγο χρήσης του inline keyword. Απλώς το βιβλίο C++ αφιέρωσε λίγο χώρο και παραδείγματα για αυτό, οπότε θεώρησα πως είναι σημαντικό. Απλά μάλλον καλό είναι να γνωρίζουμε πως η C++ προσπαθεί να κάνει inlining για definitions μικρών μεθόδων στο ίδιο translation unit.

Η C++ και η C99 κάνουν handle τα extern inlines με διαφορετικό τρόπο. Ένα σχετικό άρθρο στο Dr.Dobb's: http://www.drdobbs.com/the-new-c-inline-functions/184401540

 

Επίσης, βαριέμαι να το επιβεβαιώσω τώρα, αλλά από ότι θυμάμαι στον gcc μόνο το -Ο3 flag τον κάνει force να χρησιμοποιήσει τα inline hints του προγραμματιστή.

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Συμφωνώ. Δε μου πέρασε από το μυαλό.

Ίσως σε μπέρδεψε η C. Στην C (υπό συνθήκες ανάλογα με τι scope έχει δηλωθεί) ο compiler δημιουργεί _και_ την κανονική assembly της συνάρτησης οπότε στην περίπτωση που επιλεγεί να μην γίνει inline, ο linker βλέπει την κανονική εκδοχή και όλα παίζουν τζάμι.

 

Όταν τύχει και σου βαρέσει κάποιος κώδικας, τότε καταλαβαίνεις ποια είναι η πραγματική διαδικασία και λειτουργία της inline.

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...