2010-05-06 6 views
6

Nel suo FAQ, Bjarne Stroustrup afferma:Quali sono i preprocessori di lunghezza/limite C come strumento di creazione del linguaggio? Dove posso saperne di più su questi?

Per costruire [Cfront, il primo C++ compilatore], ho usato C a dare una "C con classi" -a-C preprocessore. "C with Classes" era un dialetto C che divenne l'antenato diretto di C++ ... Ho quindi scritto la prima versione di Cfront in "C con Classi".

Quando ho letto questo, suscitato il mio interesse per il C preprocessore. Avevo visto le sue macro capacità come adatte a semplificare espressioni comuni, ma non avevo pensato alla sua capacità di aggiungere in modo significativo sintassi e semantica al livello che immagino portasse le classi in C.

Così ora ho alcune domande sulla mia mente:

  1. Ci sono altri esempi di questo approccio bootstrap una lingua viva, fuori di C?

  2. L'origine dell'opera originale di Stroustrup è disponibile ovunque?

  3. Dove posso saperne di più sulle specifiche di utilizzo di questa tecnica?

  4. Quali sono le lunghezze/limiti di tale approccio? Si potrebbe, ad esempio, creare un insieme di macro del preprocessore che consenta a qualcuno di scrivere in qualcosa di Lisp/Schema significativamente simile?

+1

Date un'occhiata alla seguente domanda sulla scrittura di codice orientato agli oggetti in C http://stackoverflow.com/questions/351733/ can-you-write-object-oriented-code-in-c .... perché alcune delle implementazioni di C oop fanno un uso pesante del IIRC del preprocessore. –

+1

C con le classi era un compilatore che indirizzava il codice C, che è una [abitudine comune] (http://programmers.stackexchange.com/a/257873/40065). La menzione del preprocessore C è confusa. –

risposta

4

Per un esempio del tipo di mostruosità di un "linguaggio" è possibile creare utilizzando il preprocessore C, uno sguardo a questo file di intestazione:

http://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh/mac.h

E 'dal codice sorgente dell'originale Shell Unix scritta da Steve Bourne e mira a trasformare C in un linguaggio simile ad Algol. Ecco un esempio di ciò che un pezzo di codice si presenta come quando lo si utilizza:

http://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh/args.c

Questo sembra un po 'strano, ma è ancora C. Può sembrare una lingua diversa, ma perché è implementato in il preprocessore, non c'è supporto sintattico per es

WHILE foo 
DO 
    SWITCH 
    .... 
    ENDSW 
OD 

è tutto molto fine e compila bene, ma così fa

WHILE foo 
DO 
    SWITCH 
    .... 
    OD 
ENDSW 
+0

Entrambi i link sembrano morti. –

+1

Sì, hai ragione. Vedrò se riesco a trovare alcuni collegamenti aggiornati – JeremyP

12

Nota che Stroustrup è non dicendo che ha usato il preprocessore C (cpp) per creare C con le classi -ha non hanno. Ha scritto il proprio preprocessore usando C. E Cfront era un vero compilatore, non un preprocessore. Il preprocessore C è infatti profondamente inadatto allo sviluppo del linguaggio, in quanto non ha alcuna capacità di analisi.

+2

Poche abilità di parsing. Gli elenchi di parametri macro richiedono un po 'di elaborazione che si eleva al livello di analisi. Per gestire 'FOO ((x, y, z), a, b," me, me stesso, e io ")' deve fare la parentesi di profondità. – nategoose

+0

@nategoose Naturalmente è in grado di analizzare macro del preprocessore C: è ciò che è stato progettato per fare! Ma per scrivere cose come C With Classes, deve essere in grado di analizzare il codice C With Classes, e non può farlo. –

+0

Sono abbastanza sicuro che CPP sia Turing-Complete. Sembra che ciò renderebbe possibile l'analisi, se non proprio facile. –

4

Il preprocessore C non è quello che stai cercando. Non può aggiungere sintassi e semantica come ha fatto Cfront.

Cfront era un compilatore effettivo che traduceva C con Classes, in seguito C++, in C. Era un preprocessore solo per il fatto che veniva eseguito prima del compilatore C. Ho usato un programma chiamato f2c una volta per tradurre il codice FORTRAN 77 in codice C. Ha funzionato sullo stesso principio.

Ci sono linguaggi come Common Lisp con potenza macro sufficiente per aggiungere nuova sintassi e semantica e linguaggi come Forth dove il sistema è abbastanza flessibile da contenere le modifiche, ma non funzionerà per la maggior parte delle lingue.

1

Sembra che il suo preprocessore "C with Classes" -to-C sia non la stessa cosa del preprocessore C standard, dal momento che parla specificamente della scrittura di questo preprocessore stesso.

Il preprocessore C è molto limitato. Può essere usato per creare stenografie per espressioni comuni, ma questo è tutto. Quando si tenta di definire nuovi costrutti del linguaggio con esso, diventa rapidamente più ingombrante e fragile.

2

Penso che Objective-C sia partito allo stesso modo. Era un preprocessore che creava un codice C che veniva poi passato a un compilatore C. Ma non è stato il preprocessore THE C nel senso di #define FOO, è stato eseguito come un passaggio aggiuntivo prima o dopo il preprocessore C standard. Il risultato di qualsiasi numero di passaggi del preprocessore può quindi essere inviato al compilatore C.

3

Come detto da altri, C++ non è stato creato utilizzando il preprocessore C (CPP).

Detto questo, è possibile fare folle cose con il CPP e la ricorsione; Sono carina certo che Turing è completo. Le librerie che sto per collegare per utilizzare un sacco di trucchi brutti per ottenere un comportamento interessante. Anche se è possibile creare un tipo di eleganza in cima, molti potrebbero considerarlo uno Turing Tarpit.

Per un moderato introduzione a questa roba, prova Cloak.

Per andare più in profondità, guarda

  • Boost - "cross platform", ma più brutta per esso; parte della popolare libreria C++
  • Chaos - followup da Boost-pp ragazzo, ma supporta solo strumenti C99-compliant, ergo più elegante
  • Order - da quello che posso dire, un linguaggio Lisp-like, ispirato da Chaos , costruito su CPP puro

Es. con Order o Chaos, puoi scrivere un generatore di sequenze di Fibonacci ricorsivo in puro CPP.

+0

Il Chaos supporta anche i preprocessori conformi a C89. –

0

Suggerisco di iniziare con lo GCC Macros documentation che fornisce un po 'di informazioni interessanti sull'implementazione GCC del preprocessore C.

Clay Bridges nella sua risposta fornisce un paio di esempi di utilizzo del preprocessore C. Quello sulla lingua dell'Ordine è interessante con una serie di esempi. L'autore dell'Ordine fa emergere un problema in cui si è imbattuto, le implementazioni del preprocessore C potrebbero non implementare completamente standard più recenti.

In generale l'utilizzo del preprocessore C per sviluppare una sorta di linguaggio bastardo come quello che ha fatto Steve Bourne durante la scrittura di Bourne Shell per Unix è un'attività che considererei motivi idonei per la consegna, seguita da più sessioni di water boarding.

La cosa principale da ricordare sul preprocessore C è che sta manipolando i token di testo. Quindi il preprocessore C consentirà un po 'di armeggiare con la sintassi. Ad esempio la seguente macro, che compila con Visual Studio 2005 senza errori, mostra la manipolazione del testo non intuitiva possibile.

#define TESTOP(a,x,y,op,z) a (x) op (y); z 

void f(void) 
{ 
    int i = 0, j = 5; 

    TESTOP(,i,j,+=,); 
    TESTOP(,i,(j + 2),+=,); 
    TESTOP({,i,(j + 2),+=,}); 
} 

Tuttavia si ha bisogno di comprendere e risolvere alcuni dei limiti del preprocessore C quando spingendo i confini. Vedere lo GCC topic Macro Pitfalls per alcuni dei problemi da considerare.

E il Preprocessore C può essere utilizzato come un preprocessore generale di macro e testo che ha come target alcuni strumenti diversi dal compilatore C. Ad esempio, il precedente imake utility for build automation utilizzava il preprocessore C per fornire una funzione macro estesa.

Dove ho visto il Preprocessore C utilizzato in modo più efficace era nella semplificazione di codice e dichiarazioni complesse.

Un caso che ho visto è stato l'utilizzo del preprocessore C per fornire un linguaggio macchina di stato che è stato utilizzato per creare strutture dati e dati per descrivere una macchina a stati. Le strutture dati risultanti sono state quindi utilizzate come argomento per una funzione della macchina a stati. Ciò consentiva di scrivere più procedure di macchina a stati diversi nella lingua definita da C Preprocessore con l'elaborazione della macchina a stati eseguita da una singola funzione.

Microsoft, nel proprio Microsoft Foundation Classes (MFC), ha utilizzato il preprocessore C per nascondere alcuni dettagli di messaggistica di MFC. Una volta che ci si abitua a qualcosa di simile a quanto segue è ragionevolmente facile da leggere. Dal momento che l'IDE di Visual Studio disponeva di strumenti per generare e modificare il codice utilizzando i macro, era piuttosto semplice per il programmatore.

BEGIN_MESSAGE_MAP(CFrameworkWndDoc, CWindowDocument) 
    //{{AFX_MSG_MAP(CFrameworkWndDoc) 
    ON_WM_CHAR() 
    ON_WM_TIMER() 
    ON_MESSAGE(WU_EVS_DFLT_LOAD, OnDefaultWinLoad) 
    ON_MESSAGE(WU_EVS_POPUP_WINDOW, OnPopupWindowByName) 
    ON_MESSAGE(WU_EVS_POPDOWN_WINDOW, OnPopdownWindowByName) 
    ON_MESSAGE(WM_APP_CONNENGINE_MSG_RCVD, OnConnEngineMsgRcvd) 
    ON_MESSAGE(WM_APP_XMLMSG_MSG_RCVD, OnXmlMsgRcvd) 
    ON_MESSAGE(WM_APP_BIOMETRIC_MSG_RCVD, OnBiometricMsgRcvd) 
    ON_MESSAGE(WM_APP_SHUTDOWN_MSG, OnShutdownMsgRcvd) 
    ON_MESSAGE(WM_POWERBROADCAST, OnPowerMsgRcvd) 
    ON_MESSAGE(WM_APP_SHOW_HIDE_GROUP, OnShowHideGroupMsgRcvd) 
    //}}AFX_MSG_MAP 
END_MESSAGE_MAP() 

soprattutto quando si vede ciò che le macro aspetto usato come:

#define BEGIN_MESSAGE_MAP(theClass, baseClass) \ 
     PTM_WARNING_DISABLE \ 
     const AFX_MSGMAP* theClass::GetMessageMap() const \ 
      { return GetThisMessageMap(); } \ 
     const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \ 
     { \ 
      typedef theClass ThisClass;      \ 
      typedef baseClass TheBaseClass;     \ 
      static const AFX_MSGMAP_ENTRY _messageEntries[] = \ 
      { 

    #define END_MESSAGE_MAP() \ 
      {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \ 
     }; \ 
      static const AFX_MSGMAP messageMap = \ 
      { &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \ 
      return &messageMap; \ 
     }         \ 
     PTM_WARNING_RESTORE 

// for Windows messages 
#define ON_MESSAGE(message, memberFxn) \ 
    { message, 0, 0, 0, AfxSig_lwl, \ 
     (AFX_PMSG)(AFX_PMSGW) \ 
     (static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > \ 
     (memberFxn)) }, 

#define ON_WM_TIMER() \ 
    { WM_TIMER, 0, 0, 0, AfxSig_vw, \ 
     (AFX_PMSG)(AFX_PMSGW) \ 
     (static_cast< void (AFX_MSG_CALL CWnd::*)(UINT_PTR) > (&ThisClass :: OnTimer)) },