Scrittura di codice generico quando il target è un compilatore C

6

Ho bisogno di scrivere alcuni algoritmi per un microcontroller PIC. AFAIK, gli strumenti ufficiali supportano l'assemblatore o un sottoinsieme di C.

Il mio obiettivo è scrivere gli algoritmi in modo generico e riutilizzabile senza perdere runtime o prestazioni di memoria. E se possibile, vorrei farlo senza aumentare troppo i tempi di sviluppo e compromettendo molto la leggibilità e la manutenibilità.

Ciò che intendo per generico e riutilizzabile è che non voglio impegnarmi a tipi, dimensioni di array, numero di bit in un campo bit ecc. Tutte queste specifiche, IMHO, puntano a modelli C ++, ma non c'è alcun compilatore per per il mio obiettivo. La metaprogrammazione macro C è un'altra opzione, ma, a mio parere, riduce notevolmente la leggibilità e aumenta i tempi di sviluppo.

Credo che quello che sto cercando sia un discreto traduttore da C ++ a C, ma mi piacerebbe sentire qualsiasi altra cosa che soddisfi i requisiti di cui sopra. Forse un traduttore di un altro linguaggio di alto livello in C che produce codice molto efficiente, forse qualcos'altro.

Tieni presente che non ho nulla contro C, desidero solo che fossero disponibili modelli in esso.

    
posta enobayram 11.11.2013 - 06:21
fonte

2 risposte

17

A meno che tu non abbia almeno 3 casi d'uso in cui gli algoritmi saranno usati con diversi tipi e dimensioni, non farai comunque un buon lavoro di genericità. Quindi non preoccuparti troppo.

Consiglierei di scriverlo in chiaro C usando typedefs personalizzato per tutti i tipi (ad eccezione di cose che sono ovviamente size_t , int o simili) e #define s per qualsiasi dimensione pertinente. Di quanto tu possa regolare facilmente gli algoritmi cambiando quelle definizioni. Puoi inserire le definizioni in un'intestazione che puoi sovrascrivere facilmente per i casi d'uso.

Puoi anche usare il sistema di modelli del povero uomo del C, il preprocessore. Come definire una macro, otterrà un tipo ed espanderà fino alla definizione della funzione per quel tipo. Oppure definire i parametri da espandere, includere un'intestazione / fonte per definire le funzioni e annullare di nuovo i parametri. Più tardi non convoluta la diagnostica e probabilmente è più facile da gestire.

Qualcosa come:

  • sort.h:

     void sort_##SORT_TYPE(SORT_TYPE *begin, SORT_TYPE *end);
    

    IIRC avrai bisogno di alcune macro wrapper per ottenere la definizione estesa del numero di volte appropriato.

  • sort.c:

     void sort_##SORT_TYPE(SORT_TYPE *begin, SORT_TYPE *end)
     {
         // whatever
         if(SORT_LESS(i, j)) // ...
         // ..
     }
    
  • utilizza:

     #define SORT_TYPE int
     static inline int less(int x, int y) { return x < y; }
     #define SORT_LESS less
     #include "sort.h"
     #include "sort.c" // you only do this in a .c file
     #undef SORT_TYPE
     #undef SORT_LESS
    
risposta data 11.11.2013 - 09:59
fonte
0

Il modo migliore per gestirlo è scrivere effettivamente i tuoi algoritmi in uno pseudo-codice indipendente dalla lingua. Quindi, puoi creare un'implementazione di riferimento in una lingua a scelta, come C, notando attentamente i casi limite che rendono questa implementazione legata a un particolare modello di memoria, modello di tipo di dati, dimensioni dei byte, capacità di gestione dei bit, condizioni di uscita loop stranezza (pensa floating point) overflow di dimensioni, ecc. ecc.

Il motivo per cui questa è una buona strategia è che non si otterrà mai la portabilità reale con qualsiasi insieme di strumenti linguistici conosciuti, compilatori, compilatori incrociati, diagrammi, prototipi. Non è veramente trattabile.

Una volta create alcune implementazioni di riferimento, idealmente in linguaggi molto diversi (pensate a Lisp o Prolog) sarete in grado di identificare, comprendere e anticipare in modo più completo il modo in cui il vostro codice si adatta alle diverse piattaforme; e impara a scrivere pseudo-codice, muovi con attenzione verso un'implementazione e traccia i casi limite e le dipendenze.

Dovresti mantenere gli artefatti mentre scendi lungo questo percorso di implementazione, poiché questi, come lo pseudo codice di alto livello, ti aiuteranno a progettare un'attribuzione di riferimento alternativa, adatta a una piattaforma futura arbitraria.

Questo approccio ti consente di aggiungere e sottrarre arbitrariamente le dipendenze, soppesando la loro utilità rispetto ai loro possibili futuri effetti di lock-in. Ad esempio, se decidi di utilizzare un sottosistema di memoria virtuale, come la JVM, dovrai sostanzialmente riscrivere e refactare tutto se decidi di implementare lo stesso pseudo-codice su un processore embedded ....

    
risposta data 11.11.2013 - 16:57
fonte

Leggi altre domande sui tag