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

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

Δημοσ.

Όχι βρε.

 

Εννοώ:

 

Θα έχεις ένα function pointer struct/array (ό,τι σε βολεύει, αν και εμένα θα με βόλευε το struct για καλύτερη χρήση με τα ονόματα των πεδίων, δηλαδή θα είναι πιο κατανοητό στο διάβασμα από τον άνθρωπο).

 

Αυτή η συλλογή από function pointers θα είναι οι ρουτίνες σου που θα μπορούν να παράγουν λάθη που θες να τα στείλεις κάπου. Να τα διαχειριστείς.

 

Όταν θα θες να καλέσεις μία από αυτές τις ρουτίνες, δεν θα την καλείς απευθείας, αλλά από την ρουτίνα που θα διαχειρίζεται τα λάθη. Δηλαδή, σε αυτή την ρουτίνα θα της περνάς το πεδίο (για struct) που θες να καλέσεις μαζί με τα ορίσματα που θα παίρνει (έχει λίγο δουλίτσα στο να ταιριάξεις τα ορίσματα από τα typedefs των function pointers για τα struct members, εάν θες να τηρείς ένα συγκεκριμένο τύπο κτλ).

 

Αυτή η ρουτίνα θα ελέγχει την g_errorVariable (ή όπως έχεις ονομάσει το global σου) και αναλόγως εάν πρέπει θα περνάει τα ορίσματα στο function pointer που κλήθηκε.

 

Εάν κάποια επιστρέψει λάθος (που μπορεί να το επιστρέφει ή να καλεί με κάποιο όρισμα μία υπορουτίνα της ρουτίνας που διαχειρίζεται τα λάθη) τότε η ρουτίνα θα ξέρει τι να κάνει με αυτό και τι να καλέσει μετά.

 

Ο λόγος που στο προτείνω να το κάνεις έτσι είναι ότι θα μπορείς να επεκτείνεις τον κώδικα σου με παραπάνω ρουτίνες απλά βάζοντας νέα πεδία στο struct. Επίσης, αν και θα έχεις ένα level of indirection παραπάνω (μπορεί και δύο :P ), θα διαχειρίζεσαι κεντρικά τα λάθη. Επίσης, εάν σχεδιάσεις καλά το τι επιστρέφουν κάθε ρουτίνες για το λάθος ή εισάγεις κάπου το να καλούν ένα συγκεκριμένο function pointer που θα καλεί με την σειρά του την ρουτίνα διαχείρισης λαθών τότε θα έχεις μία κοινή λογική και ίσως μπορέσεις να αποφύγεις την global μεταβλητή σου.

 

 

Υ.Γ. Το προηγούμενο λίγο παραλλαγμένο... γιατί δεν το είχα σκεφτεί καλά (όχι πως έχω αυτό :P και πολλά άλλα... αλλά δεν βαριέσαι!!! :D )

Δημοσ.

Timon, με έστειλες!!!! :lol:

 

Αν και δεν είμαι σίγουρος, νομίζω πως μιλάς για δημιουργία ενός πολύ χαμηλού layer, αποτελούμενο αποκλειστικά από ρουτίνες παραγωγής σφαλμάτων (ας το πούμε error-layer), οι οποίες θα καλούνται (έμμεσα) από μια κεντρική ρουτίνα. Δηλαδη, αυτή η κεντρική ρουτίνα (ας την πούμε: errprint_central() ) θα παίρνει ως όρισμα έναν function-pointer για την προς-κλήση ρουτίνα, μαζί με τυχόν παραμέτρους που χρειάζεται αυτή, αλλά αυτός ο function pointer αντί να περνιέται απευθείας θα περνιέται επίσης έμμεσα, ως index ενός array/struct από pre-defined function pointers (ο καθένας εκ των οποίων θα δείχνει σε διαφορετική ρουτίνα του error-layer).

 

Το 'πιασα σωστά; Αν ναι, θέλεις να με ... πεθάνεις;;;;; :lol:

 

Πέρα από την πλάκα, πως γίνεται το ταίριασμα ορισμάτων της errprint_central() με τις παραμέτρους που περιμένει η προς-κλήση ρουτίνα; Σου είναι εύκολο να γράψεις το πρότυπο της errprint_central() καθώς και τον ορισμό των 2-3 πρώτων πεδίων του struct από function pointers (που θα δείχνουν σε συναρτήσεις με διαφορετικό πλήθος παραμέτρων );

 

ΥΓ1. Μάλλον κάτι δεν έχω πιάσει σωστά (ακόμα).

 

ΥΓ2. Η ιδέα είναι εξαιρετική, μου αρέσει γιατί είναι πολύ δομημένη! Για να την εφαρμόσω πρέπει να ξαναγράψω από την αρχή παραπάνω από το μισό πρόγραμμα (που δεν νομίζω να το κάνω) αλλά έχω τρελό ενδιαφέρον να καταλάβω ακριβώς πως το έχεις σκεφτεί (για μελλοντική χρήση), διότι κάπου το χάνω στην υλοποίηση που προτείνεις.

 

...

Ακριβώς επειδή ξεκίνησε (κι έχει παραμείνει) με απλοϊκή προσέγγιση, όχι μόνο είναι μακρινάρι αλλά δεν είναι και καθόλου ευέλικτη. Με πιο advanced σχεδιασμό εξαρχής θα μπορούσε αφενός να είναι η μισή σε κώδικα και αφετέρου να είναι πανεύκολο να προστεθεί π.χ η περίπτωση διαφορετικού χρωματισμού σε byte που είναι bookmarked.

 

Πιο advanced σχεδιασμός σε αυτή την περίπτωση, σημαίνει το κάθε byte του buffer να μην είναι απλά ένα uint8_t αλλά μια δομή που θα περιγράφει (και) την τρέχουσα κατάστασή του (π.χ isprintable, iszeroed, iscurrent, isbooked, κλπ) η οποία θα ενημερώνεται real time, αντί να γίνονται κάθε φορά οι υπολογισμοί που κάνει τώρα η ρουτίνα σε ΚΑΘΕ τύπωμα ενός byte στην οθόνη.

 

Τώρα θα πρέπει να το κάνω έτσι κι αλλιώς, γιατί όπως είναι δεν μπορώ να χρωματίσω bytes που είναι bookmarked χωρίς να προσθέσω κι άλλον υπολογισμό μέσα στην ρουτίνα (που για ΚΑΘΕ byte θα πρέπει να το συγκρίνει με τη λίστα των bookmarks... απαράδεκτο!).

 

Η αλλαγή της δομής του byte θα επιφέρει αλλαγές σε περισσότερο από το 60% του υπάρχοντος κώδικα.

...

Ποτέ μη λες ποτέ, δεν λέει η παροιμία; Τελικά ίσως δεν είναι και τόσο άσχημη ιδέα το να ψάχνει την bookmark-list σε κάθε επανάληψη των 2 for-loops (ένα για το hex pane κι ένα για το ascii pane) για να χρωματίζει τα bytes που είναι bookmarked.

 

Ο βασικός λόγος είναι πως η χρήση struct Byte {uint8_t byte; bool isbook}; για το κάθε byte αντί για απλό uint8_t, διπλασιάζει την απαιτούμενη μνήμη κατά το φόρτωμα του αρχείου. Δηλαδή ένα αρχείο μεγέθους 1 Gb με αυτό τον τρόπο θα χρειαστεί 2Gb μνήμης.

 

Δεδομένου πως σε μια οθόνη απεικονίζονται 704 bytes (22 γραμμές X 32 bytes... από 16 για κάθε pane) και πως η bookmark-list αποτελείται από 16 θέσεις, στο worst-case χρειάζονται 9152 έλεγχοι, σε κάθε draw_page()... πολύ μικρό αντίτιμο συγκριτικά με τον διπλασιασμό μνήμης που χρειάζεται ο άλλος τρόπος.

 

Οπότε θα το αφήσω έτσι (και ίσως μελλοντικά βελτιστοποιήσω την αναζήτηση της bookmark-list, που τώρα γίνεται σειριακά... ίσως κάνω και την εκτύπωση και των 2 panes σε ένα μόνο loop, αν και θα βγει ακόμα πιο μπερδεμένος ο κώδικας ).

Δημοσ.

Το struct δεν είναι τίποτα. Π.χ.

 

>
typedef void (*foo1_t) (int int11, int int12);
typedef void (foo2) (in int21, int int22, float float21);

typedef struct
{
 foo1_t    theFoo1FunctionPointer;
 foo2_t    theFoo2FunctionPointer;
}fcnPointers_t;

 

Και όταν θες να το χρησιμοποιήσεις:

 

>
void theFoo1FcnPointer (int int11, int int12);
void theFoo2FcnPointer (int int21, int int22, float float21);

fcnPointers_t fooFcnPointerStruct=
{
 theFoo1FcnPointer,
 theFoo2FcnPointer
};

 

Αυτό το struct θα είναι static στο .c αρχείο της ρουτίνας που κάνεις διαχείριση λαθών.

Το μόνο που μένει είναι η ευρετηρίαση των ορισμάτων του struct η οποία θα γίνεται με κάποιο όρισμα της ρουτίνας που θα διαχειρίζεται τα λάθη.

 

Δηλαδή, θα έχεις έναν "φύλακα" και θα του λες:

 

-Πάρε αυτά (τα ορίσματα για την ρουτίνα που θες να καλέσεις) και κάλεσέ μου αυτή την ρουτίνα

 

Ο "φύλακας" αυτός, θα ελέγχει εάν παίζει κάποιο λάθος και εάν όχι τότε θα καλεί ό,τι του είπαν να καλέσει.

Αλλιώς, θα ξέρει από ποια κλήση έγινε το λάθος και θα κάνει ό,τι νομίζει.

 

Ο λόγος για το struct είναι πως μπορείς να το επαναχρησιμοποιήσεις όπως ακριβώς είναι, απλά προσθέτοντας μέλη ή κάνοντας NULL μέλη που δεν θες να χρησιμοποιήσεις. Ή, ακόμα, να αλλάξεις το που δείχνει κάποιο μέλος του αναλόγως την εφαρμογή.

Δημοσ.

Στην ευρετηρίαση των ορισμάτων είναι που κολλάω (τόσο στον ορισμό της όσο και στους ελέγχους ταιριάσματος που θα κληθεί να κάνει ο guard). Δεν το πιάνω πως υλοποιείται σε κώδικα αυτό.

  • 2 εβδομάδες αργότερα...
Δημοσ.

Λοιπόν αγαπητέ migf1...

 

επειδή σου χρωστάω μία απάντηση αλλά δεν είχα τον χρόνο (μέσος όρος ωρών ύπνου/βράδυ την last week = 2hrs) σου απαντάω τώρα. :)

 

 

 

Λοιπόν:

 

ας θεωρήσουμε ότι έχουμε κάποια πρότυπα συναρτήσεων που κάνουν μία λειτουργία και τα αντίστοιχα typedefs τους, ήτοι:

 

>
typedef void (*function1_t)    (int a, int ;
typedef void (*function2_t)    (typeA_t theTypeA, typeB_t theTypeB);
typedef void (*function3_t)    (float aF, float bF);

 

όπου typeA_t και typeB_t είναι δύο typedefs που έχεις κάπου (μέσα στο ίδιο .h π.χ.)

 

Ορίζεις ένα type:

 

>
typdef struct
{
   function1_t    theFunction1;
   function2_t    theFunction2;
   function3_t    theFunction3;
}myCallbacks_t;

 

Στο .c αρχείο σου, που θα έχεις την διαχείριση, κάνεις instantiate το myCallbacks_t ως εξής:

 

>
myCallbacks_t    theCallbacks=
{
   theActualCb1,
   theActualCb1,
   NULL
};

 

όπου, έβαλα NULL γιατί μπορεί να μην θες να χρησιμοποιήσεις το τρίτο στοιχείο του myCallbacks_t.

 

Το theCallbacks το έχεις στο κυρίως αρχείο που κάνεις την διαχείρισή σου, όπου και εκεί μέσα ορίζεις τις theActualCb1, theActualCb2.

 

Μέχρι εδώ νομίζω ότι δεν έχεις κάποιο πρόβλημα αλλά δεν έχεις καταλάβει και τι κάνουμε. Υπομονή :)

 

Στο αρχείο όπου θα ορίσεις τα theActualCb, θα έχεις μόνο την λογική της διαχείρισης. Το parsing σε αυτές τις συναρτήσεις, θα το κάνεις από αλλού.

 

 

Εκεί που θα κάνεις το parsing, θα έχεις και το myCallbacks_t.

 

Επίσης, θα πρέπει να δημιουργήσεις τον τύπο myCallbacks_t (ως theCallbacks) κάπου για να μπορεί να το βλέπει το αρχείο που κάνεις το parsing.

 

Έτσι, θα έχεις μία function που θα δέχεται τα μηνύματα από όπου θες. Αυτή, θα κάνει το ανάλογο parsing αναλόγως το μήνυμα που έχει λάβει.

 

Εάν το μήνυμα είναι για function που θες να έχεις error handling, τότε θα καλείς μία άλλη function στην οποία θα της δίνεις έναν δείκτη σε τύπο myCallbacks_t και το μήνυμα (το οποίο θα είναι καλό να είναι σε typedef struct για να έχεις ομοιομορφία). Αυτή η function, θα βρίσκει σε ποια να το στείλει αντιστοιχώντας τα πεδία του myCallbacks_t με τα theActualCb, τα οποία θα τα ορίζεις στο άλλο αρχείο όπου θα έχεις και την λογική της διαχείρισης.

 

Δηλαδή, θα έχεις την function

 

>
void foo (theMessage incomingMessage, myCallbacks_t *pCbs);

 

Όπου στην υλοποίησή της θα μπορείς να έχεις κάτι σαν:

 

>
pCbs->theFunction1(incomingMessage.theIntA, incomingMessage.theIntB);

 

μετά από κάτι σαν:

 

>
switch(theFlag)
{
   case(theFlagForFunction1):
       if (pCbs->function1)
       {
           pCbs-> κτλ κτλ
       }
}

 

Έτσι, θα μπορείς να αλλάζεις την λογική χωρίς να αλλάζεις το parsing ή να αλλάζεις το parsing χωρίς να αλλάζεις την λογική (αυτό λίγο πιο δύσκολα).

 

Εάν έχεις απορία... εδώ είμαι :)

 

Μπορούμε να τα πούμε και από p.m. όπου μπορώ να σου πω που θα μπορούσες να βρεις παρόμοιες υλοποιήσεις αυτής της λογικής από κώδικα που παίζει έξω.

Δημοσ.

Thanks αρχηγέ μου, θα το κοιτάξω με ησυχία κι αν έχω τίποτα "θέματα" θα σε ενημερώσω (πιθανότατα σε p.m). Thanks και πάλι!

 

ΥΓ. Btw, το έχω προχωρήσει το HexView με το αρχικό error-handling, μάλλον μέσα στο Σ/Κ θα το ποστάρω για beta-testing (για όποιον επιθυμεί προφανώς).

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

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

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

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

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

Σύνδεση

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

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