Δημοσιεύτηκε: 07 Μάιος 2012, 18:50
Ilias95 έγραψε:
...
Το πακετάρισμα / διανομή του προγράμματος είναι κάτι με το οποίο θα βρεθώ αντιμέτωπος πολύ, πολύ αργότερα.
Οπότε ίσως είναι καλύτερα να ασχοληθώ περισσότερο με το ίδιο το πρόγραμμα που νομίζω είναι πιο ουσιαστικό.
Στο κάτω, κάτω δεν είναι και κάποια σοβαρή εφαρμογή που χρησιμοποιείται από πολλούς χρήστες και επιβάλετε να είναι πολύ εύκολη η εγκατάσταση και σε όλα τα λειτουργικά συστήματα.
Όσο πιο αυτόνομο το κάνεις το πρόγραμμά σου, τόσο πιο cross-platform γίνεται (και σαν κώδικας και σαν εγκατάσταση). Δηλαδή, όσο λιγότερο το βασίζεις σε εξωτερικές βιβλιοθήκες, τόσο αυξάνεις τις πιθανότητες να γίνεται παντού compile. Ταυτόχρονα τόση περισσότερη δουειά πρέπει να κάνεις, αφού θα πρέπει να υλοποιήσεις μόνος σου την λειτουργικότητα των εξωτερικών βιβλιοθηκών που ΔΕΝ χρησιμοποιείες
έγραψε:
Αν κατάλαβα καλά δηλαδή οι εκτυπώσεις μηνυμάτων θα γίνονται κάπως έτσι;
- Μορφοποιημένος Κώδικας: Επιλογή όλων
if (n == 3)
puts(messages[157]);
else if (n == 2)
puts(messages[158]);
else
puts(messages[159]);
Δεν χαλάει πολύ η readability;
Είναι αρκετά πιο σύνθετο, αλλά το readability χαλάει έτσι κι αλλιώς. Είναι trade-off (όπως τα περισσότερα στον προγραμματισμό).. στην προκειμένη περίπτωση είναι trade-off μεταξύ readability & δουλειάς έναντι ευκολίας τόσο σε επίπεδο μεταγλώττισης από τρίτους όσο και εγκαταστασης/χρήσης απλούς χρήστες. Για παράδειγμα, για έναν χρήστη, όταν θέλει να αλλάξει γλώσσα στο πρόγραμμά σου, αντί να αλλάζει environment variable θα διαλέγει τη γλώσσα μέσα από το ίδιο το πρόγραμμα (ή έστω θα του περνάει ένα command-line argument).
Για τον κώδικα τώρα, ένας (σχετικά) απλός τρόπος είναι ο εξής...
Ας πούμε ότι συνολικά στο πρόγραμμά σου τυπώνεις συνολικά 3 strings:
- Κώδικας: Επιλογή όλων
"What is your name?"
"Hello %s, welcome to my little program"
"Your name reversed is: %s"
Φτιάχνεις ένα απλό αρχείο κειμένου με το καθένα από τα παραπάνω strings σε μια γραμμή, χωρίς τα εισαγωγικά και το ονομάζεις ας πούμε: hunt_en.txt ...
- Μορφοποιημένος Κώδικας: Επιλογή όλων
-
What is your name?
Hello %s, welcome to my little program
Your name reversed is: %s
Μέσα στο πρόγραμμά σου έχεις έναν πίνακα από 3 strings, ας τον πούμε: msgout κι έναν enumerator για τα 3 striings, ας τον πούμε MsgId...
- Μορφοποιημένος Κώδικας: Επιλογή όλων
-
enum MsgId {
MSG_WHAT 0,
MSG_HELLO 1,
MSG_REV 2,
/* not an id, just their total count */
MAX_MESSAGES
};
int main( void )
{
char *msgout[ MAX_MESSAGES ] = { NULL };
Το επόμενο βήμα είναι να φτιάξεις μια ρουτίνα η οποία θα παίρνει σαν όρισμα τον πίνακα msgout και ένα filename, η οποία θα διαβάζει το αρχείο γραμμή-γραμμή και θα βάζει το string στην αντίστοιχη θέση του msgout...
- Μορφοποιημένος Κώδικας: Επιλογή όλων
-
Bool load_messages( char *msgout[ MAX_MESSAGES], const char *fname )
{
char fline[ 512+1] = {'\0'};
FILE *fp = NULL;
enum MsgId i = 0, j;
if ( !msgout || NULL == (fp = fopen( fname, "r")) )
return false; /* ενδεχομένως και κάποιο error-message εδώ */
for (i=0; i < MAX_MESSAGES && fgets( fline, 512+1, fp); i++ )
{
msgout[ i ] = malloc( (strlen(fline) + 1) * sizeof(char) );
if ( !msgout[ i ] )
{
for (j=i-1; j > -1; j--) {
free( msgout[ j ] );
msgout[ j ] = NULL;
}
fclose( fp );
return false;
}
strcpy( msgout[ i ], fline );
}
fclose( fp );
return true;
}
Σε αυτό το σημείο έχεις φορτωμένα όλα τα strings που χρησιμοποιεί το πρόγραμμά σου μέσα στον msgout. Οπότε, όταν θέλεις να τυπώσεις ας πούμε το "What is your name?" γράφεις...
- Μορφοποιημένος Κώδικας: Επιλογή όλων
-
printf( "%s", msgout[ MSG_WHAT ] );
Δεν είναι και τόσο τραγικό πιστεύω το readability (βασικά εξαρτάται κι από σένα, από τι ονόματα θα επιλέξεις να δώσεις στον πίνακα και στους enumerators ).
Τώρα, αν έχεις διαβάσει το όνομά του σε ένα string ας πούμε name, και θέλεις να τυπώσεις το "Hello %s, welcome to my little program" και να αλλάξεις γραμμή, γράφεις...
- Μορφοποιημένος Κώδικας: Επιλογή όλων
-
printf( msgout[ MSG_HELLO ], name );
putchar('\n');
Από εδώ και πέρα είναι πλέον τετριμένο το να αλλάζεις γλώσσα σε πραγματικό χρόνο, μέσα από το πρόγραμμά σου, αφού το μόνο που χρειάζεται είναι να καλέσεις την: load_messages() με 2ο όρισμα το όνομα του αρχείου που περιέχει τα messages της άλλης γλώσσας
Βασικά χρειάζεται πρώτα να απελευθερώσεις τη μνήμη που έχουν δεσμεύσει τα msgout[ i ] στην παλιά γλώσσα πριν φορτώσεις τη νέα. Με εκείνη τη γενική συνάρτηση απελευθέρωσης που είπα παραπάνω.
Π.χ.
- Μορφοποιημένος Κώδικας: Επιλογή όλων
-
Bool unload_messages( char *msgout[ MAX_MESSAGES ] )
{
enum MsgId i;
if ( !msgout )
return false; /* internal error, non-existent msgout */
for (i=0; i < MAX_MESSAGES; i++)
{
if ( msgout[ i ] ) {
free( mesgout[ i ] );
msgout[ i ] = NULL;
}
}
return true;
}
Οπότε, με την προϋπόθεση πως υπάρχουν τα αρχεία των γλωσσών, αλλάζεις γλώσσα on-the-fly στο run-time...
- Μορφοποιημένος Κώδικας: Επιλογή όλων
-
...
/* load greek */
unload_messages( msgout );
load_messages( msgout, "hunt_el.txt");
...
/* load italian */
unload_messages( msgout );
load_messages( msgout, "hunt_it.txt");
...
Αυτό που ΔΕΝ είναι trivial είναι να διαβάζεις/γράφεις σε διαφορετικές κωδικοποιήσεις, αλλά για αρχή η παραπάνω διδικασία είναι παραπάνω από αρκετή για να πάρεις το feeling
έγραψε:
Μια ερώτηση: Γίνεται να κάνεις serializing δομές δεδομένων της C;
Υπάρχουν εξωτερικές βιβλιοθήκες, π.χ. δες εδώ: http://stackoverflow.com/questions/3713 ... tures-in-c (οι tpl και eet είναι από τις πιο γνωστές, με την eet να μπορεί νομίζω να διαχειριστεί και nested-structs).
Αλλά για αρχή κάνε τα με απλά αρχεία κειμένου που θα τα διαχειρίζεσαι μόνος σου. Μπορείς να χρησιμοποιήσεις και τις στάνταρ συναρτήσεις της C fread()/fwrite() αλλά τα αρχεία που δημιουργούνται έτσι είναι platform-specific. Δηλαδή, ένα αρχείο που έχει γραφτεί σε little-endian platform δεν διαβάζεται σωστά σμετά σε big-endian platform.
Χρησιμοποίησε απλά αρχεία κειμένου, με τις fprintf(), fputs(), fscanf(), fgets(), fgetc(), fputc() που σου εξασφαλίζουν portability.