Vantaggi della sintassi del linguaggio da sinistra a destra

18

Ho seguito un'intervista a Herb Sutter su Channel9 e ha menzionato alla fine del video che la sintassi del linguaggio da sinistra a destra sarebbe in cima alla sua lista dei desideri per un futuro standard C ++ (anche se riconosce che la modifica di C ++ in questo modo sarebbe praticamente una bestia completamente diversa ).

Oltre a:

  • più comprensibile dagli umani, più chiaro a occhio nudo, ad esempio

    //C syntax
    
    /*pointer to function taking a pointer to function(which takes 2 integers as 
    
    arguments and returns an int), and an int as arguments and returning an int*/
    
    int (*fp)(int (*ff)(int x, int y), int b)
    
    //Go analogous syntax which is left to write
    
    f func(func(int,int) int, int) int
    
  • più facile da analizzare (porta a un migliore supporto degli strumenti come menzionato nel video - ad es. refactoring del codice)

quali altri vantaggi ci sono con una sintassi "da sinistra a destra" in un linguaggio di programmazione. So solo di Pascal e Go che utilizzano questo tipo di sintassi (e Go non fa nemmeno il percorso completo come ho capito da questo post di blog da cui ho preso anche gli esempi) Sarebbe possibile avere un linguaggio di programmazione dei sistemi con quel tipo di sintassi?

    
posta celavek 18.08.2011 - 09:51
fonte

3 risposte

12

Il vantaggio fondamentale è che l'analisi è più semplice e unica. Nota che dopo che la riga è stata analizzata, il compilatore saprà quale sia il tipo esatto, quindi da lì in poi, il modo in cui il tipo è stato definito è irrilevante.

Qualsiasi funzione che restituisce un argomento di tipo array o tipo puntatore funzione è difficile da leggere al momento:

// common way of obtaining the static size in elements of an array:
template <typename T, int N>
char (&array_size_impl( T (&)[N] ))[N];
// alternative parser:
template <typename T, int N>               // this would probably be changed also
array_simple_impl function( T [N] & ) char [N] &;

E ci sarebbero meno possibilità di fraintendimenti (come most-vexing-parse ):

// Current C++
type x( another_type() );      // create an instance x of type passing a
                               // default constructed another_type temporary?
                               // or declare a function x that returns type and takes as argument
                               // a function that has no arguments and returns another_type
// How the compiler reads it:
x function( function() another_type ) type;

// What many people mean:
x type { another_type{} };

Utilizzando un approccio simile all'inizializzazione uniforme in C ++ 0x (cioè {} per identificare l'inizializzazione). Si noti che con un approccio da sinistra a destra è molto più chiaro ciò che stiamo definendo. Molte persone (di sicuro io) sono state morse in qualsiasi momento da questo errore di parsing in passato (più di una volta), e non sarebbe stato il caso con una sintassi da sinistra a destra.

    
risposta data 18.08.2011 - 10:05
fonte
5

Come siamo arrivati qui

La sintassi C per la dichiarazione dei punti di funzione mirava a rispecchiare l'utilizzo. Prendi in considerazione una dichiarazione di funzione regolare come questa da <math.h> :

double round(double number);

Per avere una variabile punto è possibile assegnarla a con sicurezza di tipo usando

fp = round;

avresti dovuto dichiarare la variabile punto fp in questo modo:

double (*fp)(double number);

Quindi tutto ciò che devi fare è guardare come useresti la funzione e sostituire il nome di quella funzione con un riferimento di puntatore, rendendo round in *fp . Tuttavia, hai bisogno di un set extra di parens, che alcuni direbbero un po 'più confuso.

Probabilmente, questo era più facile nel C originale, che non aveva nemmeno la firma della funzione, ma non torniamo indietro, ok?

Il punto in cui diventa particolarmente sgradevole è capire come dichiarare una funzione che accetta come argomento o restituisce un puntatore a una funzione, o entrambe.

Se avevi una funzione:

void myhandler(int signo);

potresti passarlo alla funzione di segnale (3) in questo modo:

signal(SIGHUP, myhandler);

o se vuoi mantenere il vecchio gestore, quindi

old_handler = signal(SIGHUP, new_handler);

che è abbastanza facile. Ciò che è abbastanza semplice - né bello, né facile - è ottenere le dichiarazioni giuste.

signal(int signo, ???)

Bene, si ritorna alla dichiarazione della funzione e si scambia il nome con un punto di riferimento:

signal(int sendsig, void (*hisfunc)(int gotsig));

Poiché non stai dichiarando gotsig , potresti trovarlo più facile da leggere se ometti:

signal(int sendsig, void (*hisfunc)(int));

O forse no. : (

Tranne che non è abbastanza buono, perché signal (3) restituisce anche il vecchio gestore, come in:

old_handler = signal(SIGHUP, new_handler);

Quindi ora devi capire come dichiarare tutti quelli.

void (*old_handler)(int gotsig);

è sufficiente per la variabile che stai per assegnare. Tieni presente che in realtà non stai dichiarando gotsig qui, solo old_handler . Quindi questo è veramente sufficiente:

void (*old_handler)(int);

Questo ci porta ad una definizione corretta per signal (3):

void (*signal(int signo, void (*handler)(int)))(int);

Typedef to the Rescue

A quest'ora, penso che tutti saranno d'accordo che è un casino. A volte è meglio dare un nome alle tue astrazioni; spesso, davvero. Con il giusto typedef , diventa molto più facile da capire:

typedef void (*sig_t) (int);

Ora la variabile del proprio gestore diventa

sig_t old_handler, new_handler;

e la tua dichiarazione per il segnale (3) diventa solo

sig_t signal(int signo, sig_t handler);

che è improvvisamente comprensibile. Liberarsi degli * si sbarazza anche di alcune delle parentesi confuse (e dicono che Parens rende sempre le cose più facili per capire - hah!). Il tuo utilizzo è sempre lo stesso:

old_handler = signal(SIGHUP, new_handler);

ma ora hai la possibilità di comprendere le dichiarazioni per old_handler , new_handler e anche signal quando le incontri per la prima volta o devi scriverle.

Conclusione

Molto pochi programmatori C, si scopre, sono in grado di escogitare le dichiarazioni corrette per queste cose da sole senza consultare i materiali di riferimento.

Lo so, perché una volta avevamo questa stessa domanda sulle nostre domande di intervista per le persone che eseguivano il kernel e il driver del dispositivo. :) Certo, abbiamo perso molti candidati in quel modo mentre si schiantano e bruciano sulla lavagna. Ma abbiamo anche evitato di assumere persone che affermavano di aver avuto esperienze precedenti in quest'area, ma che in realtà non potevano fare il lavoro.

A causa di questa diffusa difficoltà, però, probabilmente non è solo ragionevole, ma anche ragionevole, avere una strada da percorrere per tutte quelle dichiarazioni che non richiedono più di essere un programmatore di geek tripla alfa con tre sigma sopra la media usare questo genere di cose comodamente.

    
risposta data 18.08.2011 - 16:07
fonte
4

Penso che tu abbia mancato un po 'il punto quando ti sei concentrato sul bit da sinistra a destra.

Il problema di C e C ++ è l'orrenda grammatica che hanno, che è difficile da leggere (umani) e analizzare (strumenti).

Avere una grammatica più coerente (o regolare ) rende più facile per entrambi. E l'analisi più semplice significa strumenti più semplici: la maggior parte degli strumenti attuali non ha il C ++, nemmeno l'ultimo plugin di Eclipse, poiché hanno cercato di reinventare la ruota ... e hanno fallito, e probabilmente hanno più persone del progetto OS medio.

Quindi probabilmente lo hai inchiodato concentrandoti sulla lettura e l'analisi ... e questo è un grosso problema:)

    
risposta data 18.08.2011 - 14:34
fonte

Leggi altre domande sui tag