Προς το περιεχόμενο
  • 0
Συνδεθείτε  
Kercyn

[C++] const function που καλεί non-const function

Ερώτηση

Καλησπέρα σας και καλή χρονιά.

 

Υποθέστε ότι έχουμε την παρακάτω κλάση:

class Test final
{
    public:
        RandomClass* RC;
 
        void ChangeSomething(int NewValue) const
        {
                RC->ChangeSomething(NewValue)
        }
};

όπου το RandomClass::ChangeSomething είναι non-const. Είναι "λάθος" το Test::ChangeSomething να είναι const; Δεν αλλάζει την τιμή του RC, αλλά καλεί μια non-const συνάρτηση που αλλάζει αυτό στο οποίο δείχνει το RC. Δεν υπάρχει κανένα θέμα με λάθη κλπ, απλώς τώρα που ξανακοιτάω κάποιες τέτοιες κλάσεις μου γεννήθηκε αυτή η απορία. Εσείς αν βλέπατε την Test::ChangeSomething, θα σας φαινόταν "κακό" ή παράξενο να έχει μια τέτοια συμπεριφορά; Δεν είναι ότι καλεί μια άσχετη non-const συνάρτηση ενός άλλου τυχαίου αντικειμένου, καλεί non-const συνάρτηση ενός δικού της αντικειμένου.

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες

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

  • 0

Κακό, πολύ κακό. Στον παρακάτω κώδικα η doSthElse() θα επέστρεφε πλέον κάτι διαφορετικό σε σχέση με πριν, που σημαίνει ότι η κατάσταση της Test έχει αλλάξει.

class Test final
{
    public:
        RandomClass* RC;
 
        void ChangeSomething(int NewValue) const
        {
                RC->ChangeSomething(NewValue)
        }
        
        void doSthElse() 
        {
                return RC->getSomething();
        }

};

const method πρέπει να καλεί μονάχα const methods των members της κλάσης στην οποία ανήκει.

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες
  • 0

Λάθος είναι να κάνεις πράγματα που δεν είναι "logically const", και μόνο αυτό. Δεν είναι απαραίτητο να κάνεις μόνο πράγματα που είναι "physically const".

 

Υπάρχει τρόπος μέσα από το public interface της Test, άμεσα ή έμμεσα να καταλάβει κανείς τι έκανε η ChangeSomething? (Ισοδύναμα, αν κάνεις observe το αντικείμενο Test πριν την κλήση της ChangeSomething() εξωτερικά, υπάρχει τρόπος μετά από λίγο να καταλάβεις αν έχουμε καλέσει την ChangeSomething() ή όχι?) Αν ναι, τότε είναι λάθος. Αν όχι, κανένα πρόβλημα (εξάλλου γι' αυτές ακριβώς τις περιπτώσεις δημιουργήθηκε και το mutable).

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες
  • 0

Οι κλάσεις αυτές χρησιμοποιούνται ως wrappers για να μπορούν να έχουν πρόσβαση διάφορα lua scripts σε λειτουργίες του προγράμματός μου. Αυτή είναι η μόνη τους χρησιμότητα, δεν διαχειρίζονται τίποτα στο πρόγραμμα. Οι μέθοδοί τους καλούνται μόνο από τη lua, από πουθενά μέσα στο πρόγραμμα. Γι' αυτό και όλες οι μέθοδοι αυτών των κλάσεων είναι const (με ελάχιστες εξαιρέσεις που όντως αλλάζουν κάτι μέσα στην ίδια την κλάση), επειδή είναι αυτό που είπε ο defacer "physically const", δηλαδή δεν αλλάζουν κάτι μέσα στο ίδιο το αντικείμενο. Μετά απ' αυτά που λέτε βέβαια (και έχετε δίκιο), θα βγάλω το const από συναρτήσεις που δεν είναι const στο wrapped αντικείμενο.

 

Ευχαριστώ!

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες
  • 0

Μετά απ' αυτά που λέτε βέβαια (και έχετε δίκιο), θα βγάλω το const από συναρτήσεις που δεν είναι const στο wrapped αντικείμενο.

Υπόψη ότι προσωπικά δεν είπα αυτό έτσι; Αν το wrapped αντικείμενο δε μπορεί να επηρεάσει τη συμπεριφορά του wrapper τότε το αφήνεις κιόλας το const.

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες
  • 0

Υπόψη ότι προσωπικά δεν είπα αυτό έτσι; Αν το wrapped αντικείμενο δε μπορεί να επηρεάσει τη συμπεριφορά του wrapper τότε το αφήνεις κιόλας το const.

 

Χμμ, τότε κατάλαβα λάθος. Το wrapped αντικείμενο δεν επικοινωνεί καθόλου με το wrapper, και ο wrapper απλώς κρατάει έναν pointer στο wrapped αντικείμενο. Ο wrapper απλώς καλεί μέσω του pointer συναρτήσεις του wrapped αντικειμένου. Ο λόγος ύπαρξής τους είναι ότι η Lua δέχεται/επιστρέφει συγκεκριμένα πράγματα και δε μπορώ πχ να επιστρέψω ένα glm::vec2 (2D διάνυσμα) στη Lua, πρέπει να επιστρέψω ένα std::tuple<LUA_NUMBER, LUA_NUMBER> και άλλα τέτοια.

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες
  • 0

Χμμ, τότε κατάλαβα λάθος. Το wrapped αντικείμενο δεν επικοινωνεί καθόλου με το wrapper, και ο wrapper απλώς κρατάει έναν pointer στο wrapped αντικείμενο. Ο wrapper απλώς καλεί μέσω του pointer συναρτήσεις του wrapped αντικειμένου. Ο λόγος ύπαρξής τους είναι ότι η Lua δέχεται/επιστρέφει συγκεκριμένα πράγματα και δε μπορώ πχ να επιστρέψω ένα glm::vec2 (2D διάνυσμα) στη Lua, πρέπει να επιστρέψω ένα std::tuple<LUA_NUMBER, LUA_NUMBER> και άλλα τέτοια.

 

Θα ήταν χρήσιμο να φέρεις ένα παράδειγμα γιατί μια const method του wrapper να αναγκάζεται να καλέσει μια non-cost του wrapped, και if in doubt απλά βγάλε το const (καλύτερα να μη δίνεις εγγυήσεις που θα μπορούσες παρά να παραπλανήσεις κάποιον), αλλά γενικά κράτα αυτό που είπα για logical vs physical const. Το logical αρκεί.

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες
  • 0

Έχεις δίκιο γι' αυτό με το παράδειγμα. Κάθε φορά προσπαθώ να κάνω μια ερώτηση που είναι πιο γενική από κάτι που αντιμετωπίζω για να το δω πιο σφαιρικά και γενικά αλλά πάντα χρειάζεται παράδειγμα :P

 

Έχουμε λοιπόν αυτή την κλάση (μέρος της):

class AActor
{
    public:
        const glm::vec2& GetPosition() const;
    
        void SetPosition(const glm::vec2& NewPosition);
        void SetPosition(float NewX, float NewY);

    protected:
        //! The actor's current position on the screen.
        glm::vec2 Position;
};

και έχουμε και το wrapper:

class SIActor final
{
    public:
        AActor* Actor;

        std::tuple<LUA_NUMBER, LUA_NUMBER> GetPosition() const;
        void SetPosition(LUA_NUMBER NewX, LUA_NUMBER NewY);
};

Το SIActor::SetPosition καλεί το AActor::SetPosition. Το SIActor δεν αλλάζει κάτι στο SIActor object, αλλά η κλήση στο Actor->SetPosition αλλάζει το Actor. Απ' όσο ξέρει το SIActor object, μετά την κλήση της SetPosition δεν έχει αλλάξει κάτι. Η διεύθυνση του Actor είναι αυτή που ήταν, οπότε όλα ωραία όλα καλά. Αλλά το αντικείμενο στη διεύθυνση που δείχνει το Actor έχει αλλάξει.

Αρχικά ακολουθούσα την πρώτη λογική και οι SI (Script Interface) κλάσεις είχαν const παντού (εκτός από μερικές εξαιρέσεις). Μετά όμως σκέφτηκα ότι θα ήταν πιο "λογικό" (αφού τα SI είναι wrappers) να ακολουθούν την const λογική των συναρτήσεων που καλούν από το pointed object (όπως το Actor).

Εκεί είπα να ζητήσω και μια δεύτερη γνώμη για το τι γίνεται και τι θα ήταν καλό να ακολουθείται σ' αυτές τις περιπτώσεις.

 

ΥΓ.: Στη Lua υπάρχουν γραμμές τύπου Player:SetPosition(x, y), όπου αυτές οι συναρτήσεις είναι bound με τις αντίστοιχες συναρτήσεις των SI. Η Lua ούτε μπορεί να δει ούτε ασχολείται με οτιδήποτε άλλο, η μόνη της επικοινωνία με το κυρίως πρόγραμμα είναι μόνο μέσω των SI functions

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες
  • 0

Κοιτα, το const θα βαρεσει οταν γινει call μιας non-const function σε ενα const object. Σε εσενα το Actor δεν ειναι const, ετσι δεν υπαρχει λογος να βλαεις const.

 

Btw γιατι wrapper και οχι interface;

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες
  • 0

Κοιτα, το const θα βαρεσει οταν γινει call μιας non-const function σε ενα const object. Σε εσενα το Actor δεν ειναι const, ετσι δεν υπαρχει λογος να βλαεις const.

 

Ναι δεν είχα θέμα με λάθη, πιο πολύ αναρωτιόμουν ποιο είναι το πιο "σωστό" σ' αυτές τις περιπτώσεις. const επειδή δεν αλλάζει κάτι στο object ή non-const επειδή το function που γίνεται wrap είναι non-const;

 

 

Btw γιατι wrapper και οχι interface;

 

Γιατί όπως βλέπεις τα signatures δεν είναι πάντα τα ίδια. Εκτός αν εννοείς κάτι άλλο.

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες
  • 0

Χρησιμοποιώ αυτό.

 

Του Actor συγκεκριμένα γίνεται έτσι (State είναι το Lua state)

 

State["PActor"].SetClass<SIActor, std::string>(
        "GetPosition", &SIActor::GetPosition,
        "GetKey", &SIActor::GetKey,
        "GetID", &SIActor::GetID,
        "GetProperty", &SIActor::GetProperty,
        "GetColliderID", &SIActor::GetColliderID,
        "IsEnabled", &SIActor::IsEnabled,
        "Translate", &SIActor::Translate,
        "Rotate", &SIActor::Rotate,
        "SetPosition", &SIActor::SetPosition,
        "SetRotation", &SIActor::SetRotation,
        "SetOrientation", &SIActor::SetOrientation,
        "SetProperty", &SIActor::SetProperty,
        "Enable", &SIActor::Enable,
        "Disable", &SIActor::Disable);

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες
  • 0

Με interface πιο απλο. Το SIActor το κανεις interface και το βαζεις στο Actor

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες
  • 0

Δε θα ήταν μπερδεμένα έτσι όμως; Και τι ακριβώς κερδίζω μ' αυτό, αφού θα πρέπει να υπάρχουν και οι δύο συναρτήσεις (αυτή πχ που επιτστρέφει glm::vec2 και η άλλη που επιστρέφει std::tuple).

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες
  • 0

εσυ πες μου.

typedef float lua_type;
class ILuaObject 
{
public:
	virtual void SetVal(const lua_type& val) = 0;
};

class Object : public ILuaObject
{
	int val;
public:
	void SetVal(const int& val)
	{
		this->val = val;
	}
	void ILuaObject::SetVal(const lua_type& val)
	{
		this->SetVal(static_cast<int>(val));
	}
};

Μπορεις να βαλεις και ενα perfix σε καθε function πχ si_SetVal

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες
  • 0

Ωραία, άρα κάνεις μια SetPosition η οποία θα μπορούσε να είναι const (με την έννοια ότι δε θα σε εμποδίσει ο compiler) και αμέσως μετά μπορείς να κάνεις μια GetPosition και να δεις τα definitely not const αποτελέσματα της SetPosition που έκανες μόλις πριν.

 

Άρα η SetPosition είναι κάθε άλλο παρά logically const, άρα δεν πρέπει να είναι const.

 

Απλό.  :-D

 

ΥΓ note ότι δε χρειάστηκε καν να μπει το wrapped στη συζήτηση.

Κοινοποιήστε αυτήν την ανάρτηση


Σύνδεσμος στην ανάρτηση
Κοινοποίηση σε άλλες σελίδες

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

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

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

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

Εγγραφείτε για έναν νέο λογαριασμό

Σύνδεση

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

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

Χρήσιμες πληροφορίες

Με την περιήγησή σας στο insomnia.gr, αποδέχεστε τη χρήση cookies που ενισχύουν σημαντικά την εμπειρία χρήσης.