Προγραμματισμός

Εισαγωγή στον μεταπρογραμματισμό στο C ++

Προηγούμενη 1 2 3 Σελίδα 3 Σελίδα 3 από 3
  • Μεταβλητές κατάστασης: Οι παράμετροι προτύπου
  • Κατασκευές βρόχου: Μέσω αναδρομής
  • Εκλογή διαδρομών εκτέλεσης: Με χρήση εκφράσεων υπό όρους ή εξειδικεύσεων
  • Ακέραιος αριθμητικός

Εάν δεν υπάρχουν όρια στο ποσό των αναδρομικών παραστάσεων και στον αριθμό των μεταβλητών κατάστασης που επιτρέπονται, αυτό αρκεί για τον υπολογισμό οτιδήποτε είναι υπολογιστέο. Ωστόσο, μπορεί να μην είναι βολικό να το κάνετε χρησιμοποιώντας πρότυπα. Επιπλέον, επειδή το πρότυπο instantiation απαιτεί σημαντικούς πόρους μεταγλωττιστή, η εκτεταμένη αναδρομική παρουσία επιβραδύνει γρήγορα έναν μεταγλωττιστή ή εξαντλεί ακόμη και τους διαθέσιμους πόρους. Το πρότυπο C ++ συνιστά, αλλά δεν απαιτεί να επιτρέπονται τουλάχιστον 1.024 επίπεδα αναδρομικών παραστάσεων, κάτι που αρκεί για τις περισσότερες (αλλά σίγουρα όχι όλες) εργασίες προγραμματισμού προτύπων.

Έτσι, στην πράξη, τα μεταπρογράμματα προτύπων πρέπει να χρησιμοποιούνται με φειδώ. Υπάρχουν μερικές καταστάσεις, ωστόσο, όταν είναι αναντικατάστατες ως εργαλείο για την εφαρμογή κατάλληλων προτύπων. Συγκεκριμένα, μερικές φορές μπορούν να κρυφτούν στα στελέχη πιο συμβατικών προτύπων για να συμπιέσουν περισσότερη απόδοση από κρίσιμες εφαρμογές αλγορίθμων.

Αναδρομική παρουσίαση έναντι αναδρομικών ορισμάτων προτύπων

Εξετάστε το ακόλουθο αναδρομικό πρότυπο:

πρότυπο struct Doublify {}; Πρότυπο struct Trouble {using LongType = Doublify; }; Πρότυπο struct Trouble {χρησιμοποιώντας LongType = double; }; Πρόβλημα :: Ωχ LongType;

Η χρήση του Πρόβλημα :: LongType όχι μόνο ενεργοποιεί την αναδρομική παρουσία του Ταλαιπωρία, Ταλαιπωρία, …, Ταλαιπωρία, αλλά επίσης υποδηλώνει Διπλασιάστε σε όλο και πιο περίπλοκους τύπους. Ο πίνακας δείχνει πόσο γρήγορα μεγαλώνει.

Η ανάπτυξη του Πρόβλημα :: LongType

 
Πληκτρολογήστε ΨευδώνυμοΥποκείμενος τύπος
Πρόβλημα :: LongTypeδιπλό
Πρόβλημα :: LongTypeΔιπλασιάστε
Πρόβλημα :: LongTypeΔιπλασιάστε<>

Διπλασιάστε>

Πρόβλημα :: LongTypeΔιπλασιάστε<>

Διπλασιασμός>,

   <>

Διπλασιάστε >>

Όπως δείχνει ο πίνακας, η πολυπλοκότητα της περιγραφής τύπου της έκφρασης Πρόβλημα :: LongType μεγαλώνει εκθετικά με Ν. Σε γενικές γραμμές, μια τέτοια κατάσταση τονίζει έναν μεταγλωττιστή C ++ ακόμη περισσότερο από ό, τι οι αναδρομικές καταστάσεις που δεν περιλαμβάνουν επαναλαμβανόμενα ορίσματα προτύπων. Ένα από τα προβλήματα εδώ είναι ότι ένας μεταγλωττιστής διατηρεί μια αναπαράσταση του αλλοιωμένου ονόματος για τον τύπο. Αυτό το αλλοιωμένο όνομα κωδικοποιεί την ακριβή εξειδίκευση προτύπου με κάποιο τρόπο και οι πρώτες εφαρμογές C ++ χρησιμοποίησαν μια κωδικοποίηση που είναι περίπου ανάλογη με το μήκος του προτύπου-id. Αυτοί οι μεταγλωττιστές στη συνέχεια χρησιμοποίησαν πάνω από 10.000 χαρακτήρες για Πρόβλημα :: LongType.

Οι νεότερες εφαρμογές C ++ λαμβάνουν υπόψη το γεγονός ότι τα ένθετα πρότυπα-id είναι αρκετά κοινά στα σύγχρονα προγράμματα C ++ και χρησιμοποιούν έξυπνες τεχνικές συμπίεσης για να μειώσουν σημαντικά την αύξηση της κωδικοποίησης ονόματος (για παράδειγμα, μερικές εκατοντάδες χαρακτήρες για Πρόβλημα :: LongType). Αυτοί οι νεότεροι μεταγλωττιστές αποφεύγουν επίσης τη δημιουργία ενός αλλοιωμένου ονόματος, εάν δεν απαιτείται κανένας, επειδή δεν δημιουργείται κωδικός χαμηλού επιπέδου για την παρουσία προτύπου. Ωστόσο, με όλα τα άλλα πράγματα να είναι ίσα, είναι προτιμότερο να οργανώνουμε αναδρομική παρουσίαση με τέτοιο τρόπο ώστε τα ορίσματα προτύπων να μην χρειάζεται να τοποθετούνται αναδρομικά.

Τιμές απαρίθμησης έναντι στατικών σταθερών

Στις πρώτες μέρες του C ++, οι τιμές απαρίθμησης ήταν ο μόνος μηχανισμός για τη δημιουργία «πραγματικών σταθερών» (ονομάζεται σταθερές εκφράσεις) ως ονομασμένα μέλη στις δηλώσεις τάξης. Με αυτά, θα μπορούσατε, για παράδειγμα, να ορίσετε ένα Pow3 μεταπρόγραμμα για τον υπολογισμό των δυνάμεων του 3 ως εξής:

meta / pow3enum.hpp // πρωτεύον πρότυπο για τον υπολογισμό 3 στο Nth πρότυπο struct Pow3 {enum {value = 3 * Pow3 :: value}; }; // πλήρης εξειδίκευση για τον τερματισμό του προτύπου αναδρομής struct Pow3 {enum {value = 1}; };

Η τυποποίηση του C ++ 98 εισήγαγε την έννοια των στατικών σταθερών αρχικοποιητών, έτσι ώστε το μετα-πρόγραμμα Pow3 να φαίνεται ως εξής:

meta / pow3const.hpp // πρωτογενές πρότυπο για τον υπολογισμό 3 στο Nth πρότυπο struct Pow3 {static int const value = 3 * Pow3 :: value; }; // πλήρης εξειδίκευση για τον τερματισμό του προτύπου αναδρομής struct Pow3 {static int const value = 1; };

Ωστόσο, υπάρχει ένα μειονέκτημα με αυτήν την έκδοση: Στατικά σταθερά μέλη είναι τιμές. Έτσι, εάν έχετε μια δήλωση όπως

void foo (int const &);

και το περνάτε το αποτέλεσμα ενός μεταπρογράμματος:

foo (Pow3 :: τιμή);

ένας μεταγλωττιστής πρέπει να περάσει το διεύθυνση του Pow3 :: τιμή, και αυτό αναγκάζει τον μεταγλωττιστή να δημιουργήσει και να εκχωρήσει τον ορισμό για το στατικό μέλος. Ως αποτέλεσμα, ο υπολογισμός δεν περιορίζεται πλέον σε ένα καθαρό εφέ «μεταγλώττισης».

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

Το C ++ 11, ωστόσο, εισήχθη constexpr μέλη στατικών δεδομένων και αυτά δεν περιορίζονται σε ακέραιους τύπους. Δεν επιλύουν το ζήτημα της αντιμετώπισης που τέθηκε παραπάνω, αλλά παρά το μειονέκτημα αυτό είναι πλέον ένας κοινός τρόπος παραγωγής αποτελεσμάτων μεταπρογραμμάτων. Έχουν το πλεονέκτημα ότι έχουν έναν σωστό τύπο (σε αντίθεση με έναν τεχνητό τύπο enum), και αυτός ο τύπος μπορεί να συναχθεί όταν το στατικό μέλος δηλωθεί με τον αυτόματο προσδιοριστή τύπου. Το C ++ 17 πρόσθεσε ενσωματωμένα μέλη στατικών δεδομένων, τα οποία επιλύουν το ζήτημα διεύθυνσης που αναφέρθηκε παραπάνω και μπορούν να χρησιμοποιηθούν με constexpr.

Ιστορικό μεταπρογραμματισμού

Το παλαιότερο τεκμηριωμένο παράδειγμα ενός μεταπρογράμματος ήταν ο Erwin Unruh, ο οποίος εκπροσωπούσε τότε τη Siemens στην επιτροπή τυποποίησης C ++. Σημείωσε την υπολογιστική πληρότητα της διαδικασίας δημιουργίας προτύπων και απέδειξε την άποψή του αναπτύσσοντας το πρώτο μεταπρόγραμμα. Χρησιμοποίησε το μεταγλωττιστή Metaware και το έπεισε στην έκδοση μηνυμάτων σφάλματος που θα περιείχαν διαδοχικούς πρώτους αριθμούς. Εδώ είναι ο κώδικας που κυκλοφόρησε σε μια συνεδρίαση της επιτροπής C ++ το 1994 (τροποποιήθηκε έτσι ώστε τώρα μεταγλωττίζεται σε τυποποιημένους μεταγλωττιστές συμμόρφωσης):

meta / unruh.cpp // πρωταρχικός υπολογισμός αριθμού // (τροποποιήθηκε με άδεια από το πρωτότυπο από το 1994 από τον Erwin Unruh) πρότυπο struct is_prime {enum ((p% i) && is_prime2? p: 0), i-1> :: pri); }; template struct is_prime {enum {pri = 1}; }; template struct is_prime {enum {pri = 1}; }; πρότυπο δομή D {D (άκυρο *); }; πρότυπο struct CondNull {static int const value = i; }; πρότυπο struct CondNull {static void * value; }; void * CondNull :: τιμή = 0; πρότυπο δομή Prime_print {

// κύριο πρότυπο για βρόχο για εκτύπωση πρωταρχικών αριθμών Prime_print a; enum {pri = is_prime :: pri}; κενό f () {D d = CondNull :: τιμή;

// 1 είναι σφάλμα, το 0 είναι καλό a.f (); }} πρότυπο struct Prime_print {

// πλήρης εξειδίκευση για τον τερματισμό του βρόχου enum {pri = 0}; κενό f () {D d = 0; }; }; #ifndef LAST #define LAST 18 #endif int main () {Prime_print a; a.f (); }

Εάν μεταγλωττίσετε αυτό το πρόγραμμα, ο μεταγλωττιστής θα εκτυπώσει μηνύματα σφάλματος όταν, σε Prime_print :: f (), η αρχικοποίηση του d αποτυγχάνει. Αυτό συμβαίνει όταν η αρχική τιμή είναι 1 επειδή υπάρχει μόνο ένας κατασκευαστής για το κενό * και μόνο το 0 έχει μια έγκυρη μετατροπή σε κενός*. Για παράδειγμα, σε έναν μεταγλωττιστή, λαμβάνουμε (μεταξύ πολλών άλλων μηνυμάτων) τα ακόλουθα σφάλματα:

unruh.cpp: 39: 14: σφάλμα: καμία βιώσιμη μετατροπή από "const int" σε "D" unruh.cpp: 39: 14: σφάλμα: καμία βιώσιμη μετατροπή από "const int" σε "D" unruh.cpp: 39: 14: σφάλμα: καμία βιώσιμη μετατροπή από "const int" σε "D" unruh.cpp: 39: 14: σφάλμα: καμία βιώσιμη μετατροπή από "const int" σε "D" unruh.cpp: 39: 14: σφάλμα: δεν είναι βιώσιμο μετατροπή από "const int" σε "D" unruh.cpp: 39: 14: σφάλμα: καμία βιώσιμη μετατροπή από "const int" σε "D" unruh.cpp: 39: 14: σφάλμα: καμία βιώσιμη μετατροπή από "const int" στο "D"

Σημείωση: Καθώς ο χειρισμός σφαλμάτων στους μεταγλωττιστές διαφέρει, ορισμένοι μεταγλωττιστές ενδέχεται να σταματήσουν μετά την εκτύπωση του πρώτου μηνύματος σφάλματος.

Η έννοια του μετα-προγραμματισμού προτύπων C ++ ως σοβαρού εργαλείου προγραμματισμού έγινε για πρώτη φορά δημοφιλής (και κάπως τυποποιημένη) από τον Todd Veldhuizen στην εργασία του «Χρήση μεταπρογραμμάτων προτύπων C ++». Το έργο του Veldhuizen στο Blitz ++ (μια αριθμητική βιβλιοθήκη συστοιχιών για το C ++) εισήγαγε επίσης πολλές βελτιώσεις και επεκτάσεις στον μεταπρογραμματισμό (και στις τεχνικές προτύπων έκφρασης).

Τόσο η πρώτη έκδοση αυτού του βιβλίου όσο και η Andrei Alexandrescu's Μοντέρνος σχεδιασμός C ++ συνέβαλε σε μια έκρηξη βιβλιοθηκών C ++ που εκμεταλλεύονταν μετα-προγραμματισμό βάσει προτύπων καταλογογραφώντας μερικές από τις βασικές τεχνικές που εξακολουθούν να χρησιμοποιούνται σήμερα. Το έργο Boost ήταν καθοριστικό για να διευθετήσει αυτήν την έκρηξη. Νωρίς, εισήγαγε το MPL (βιβλιοθήκη μεταπρογραμματισμού), το οποίο καθόρισε ένα συνεπές πλαίσιο για τύπος μεταπρογραμματισμού έγινε δημοφιλής επίσης μέσω του βιβλίου του David Abrahams και του Aleksey Gurtovoy Μεταπρογραμματισμός προτύπου C ++.

Πρόσθετες σημαντικές εξελίξεις έχουν γίνει από τον Louis Dionne στο να καταστήσει το μεταπρογραμματισμό συντακτικό πιο προσβάσιμο, ιδίως μέσω της βιβλιοθήκης Boost.Hana. Ο Dionne, μαζί με τον Andrew Sutton, τον Herb Sutter, τον David Vandevoorde και άλλους, ηγούνται πλέον των προσπαθειών στην επιτροπή τυποποίησης για να προσφέρουν μετα-προγραμματισμό πρώτης κατηγορίας υποστήριξη στη γλώσσα. Μια σημαντική βάση για αυτό το έργο είναι η διερεύνηση των ιδιοτήτων του προγράμματος που πρέπει να είναι διαθέσιμα μέσω προβληματισμού. Οι Matúš Chochlík, Axel Naumann και David Sankel είναι βασικοί συντελεστές σε αυτήν την περιοχή.

Οι John J. Barton και Lee R. Nackman παρουσίασαν πώς να παρακολουθούν τις διαστατικές μονάδες κατά την εκτέλεση υπολογισμών. Η βιβλιοθήκη SIunits ήταν μια πιο ολοκληρωμένη βιβλιοθήκη για την αντιμετώπιση φυσικών μονάδων που αναπτύχθηκαν από τον Walter Brown ο std :: chrono Το στοιχείο της τυπικής βιβλιοθήκης ασχολείται μόνο με την ώρα και τις ημερομηνίες και συνεισέφερε ο Howard Hinnant.

$config[zx-auto] not found$config[zx-overlay] not found