
Ting jeg savner i C
C sproget fik på et tidspunkt de mest ubrugelige enum's jeg kender i noget programmeringssprog og resultatet er at kun meget ideologiske programmører bruger dem overhovedet.
Her er et lille aktuelt eksempel hvor de kunne have været brugbare, hvis bare nogen havde tænkt sig om.
Jeg sidder med en microcontroller der skal tale MODBUS og har derfor en array med navngivne indices.
I kildeteksten ser det således ud:
static uint16_t hold[9];
#define HOLD_TEMPI 0
#define HOLD_TEMPIX 1
#define HOLD_TEMPO 2
#define HOLD_TEMPOX 3
#define HOLD_PWM 4
#define HOLD_RPM 5
#define HOLD_VCC 6
#define HOLD_VCORE 7
#define HOLD_VREF 8
Er det egentlig ikke til at tude over ?
Tænk hvis enum's havde været lavet fornuftigt, så kunne man f.eks have skrevet:
enum hold_vars {
TEMPI, TEMPIX, TEMPO, TEMPOX,
PWM, RPM, VCC, VCORE, VREF
};
static uint16_t hold[enum hold_vars];
Jeg vil vædde på at ovenstående er en temmelig triviel ting at implementere i en C-compiler, det kan næppe tage mere end 30 linier kode...
phk
Kommentarer (27)
Et hack, der smager af fisk:
[code=c]
enum hold_vars {
TEMPI, TEMPIX, TEMPO, TEMPOX,
PWM, RPM, VCC, VCORE, VREF,
_HOLD_SZ
};
static uint16_t hold[_HOLD_SZ];
[/code]
Det største problem er den svage scoping og typing. Jeg ville forvente, at enum identifiers kun kunne bruges med variable, der er erklæret af den pågældende enum type (eller arrays, med indeks af den pågældende enum type).
Det er ikke vanskeligt at implementere, det koster ikke noget i køretid, og man kan altid typecast'e sig ud af konverteringer.
Thomas
På min arbejdsplads hvor stort set alt kodes i C er enums simpelthen forbudt at bruge. Det giver nogle gange noget ganske grimt kode, men til gengæld er den porterbar til alle de platforme vi arbejder med. Fatter ikke hvorfor de ikke bare fik sat en størrelse på enums elementer fra start af, så vi kunne komme ud over alt det pjat.
Hvorfor ikke en struct?
[code=c]static struct {
uint16_t TEMPI;
uint16_t TEMPIX;
uint16_t TEMPO;
uint16_t TEMPOX;
uint16_t PWM;
uint16_t RPM;
uint16_t VCC;
uint16_t VCORE;
uint16_t VREF;
} hold;
[/code]
Og en [code=c]union[/code] uden om hvis et array skulle være nødvendigt.
Det kan være en fordel at bruge variabler i stedet, i de tilfælde hvor det er muligt:
const int HOLD_TEMPI = 0;
const int HOLD_TEMPIX = 1;
...
Det giver type checking og desuden kan deres værdier udlæses under debugging, hvilket #defines ikke kan. Hastigheds/størrelsesmæssigt giver det samme resultat undtagen i specialtilfælde. Preprocessor ting bør man i det hele taget kun bruge, hvor der ikke findes et alternativ, som er lige så godt.
I Go har de en noget underlig iota-konstruktion. iota resettes hver gang man støder på en const og inkrementeres så hver gang man anvender den. Den kan endda udelades:
const ( HOLD_TEMPI = iota HOLD_TEMPIX HOLD_TEMPO HOLD_PWM HOLD_RPM ... )
Til gengæld ser det ikke ud til at de har lyttet til PHK for sproget lader dig ikke erklære et array i antallet af elementer som er erklæret i en const-block så vidt jeg kan se.
Til gengæld kunne jeg godt tænke mig at vide hvorfor man har et behov for det array, for der kunne være en anden måde at beskrive problemet på som er bedre.
Hvad er enums egentlig til for ?
Når dette er tilladt:
enum test {
FIRST = 7,
SECOND = 7
};...og dette er muligt:
enum test {
FIRST = 0,
SECOND = 1
};
void do_stuff(enum test) {
}
int main (int argc, char ** argv) {
do_stuff(18);
return 0;
}...og dette ikke giver antallet af enum værdier:
#include <stdio.h>
enum test {
FIRST = 0,
SECOND = 1
};
int main (int argc, char ** argv) {
printf("%d\n", sizeof(enum test));
return 0;
}...så forstår jeg ikke længere, hvad jeg skal bruge enum til.
Javas version af enum giver mening (synes jeg), men C's giver en falsk tryghed (synes jeg).
Jeg bruger ikke en struct fordi MODBUS datamodellen er en array af 16 bit ints.
Ideen med at overlejre en union vil jeg slet ikke kommentere, det er hverken sikkert, kønt eller portabelt.
Poul-Henning
Hvad med Google Go - mon ikke det har gode chancer for at overtage C's rolle som foretrukkent sprog til ressourceknappe platforme? (f.eks. 8-bit sager)
Det ville jeg sætte pris på.
Java kører da på 8-bit.
Det store firma: http://www.is2t.com/en/technology-microjvm.php oooog lejos/tinyvm http://lejos.sourceforge.net/
Jeg ved ikke om denne kan 8-bit, men det er ihvertfald lavet til at køre på "the bare metal" https://squawk.dev.java.net/
Det kan self. være der ikke er nok mem. til at køre dem, det skal jeg ikke lige kunne gøre mig klog på.
/David
Nu er der jo tale om hardwarenært programmering, så jeg er lidt nysgerrig i forhold til Java her:
Hvordan angiver man absolutte adresser i Java?
Hvordan skifter man endianess i Java?
Hvordan mikser man assembler med Java?
Hvordan laver man interuptrutiner i Java?
Jeg har skrevet en del linier C på platforme, hvor der ikke er noget runtime system (f.eks. operativsystem), og jeg kan ikke rigtig få det til at gå op med den abstraktion, som Java giver. Men jeg kender ikke nok til Java på det felt.
Please enlighten me here
Thomas
Rart at blive betragtet som nuttet :-)
Bare lige for at slå det fast, så nævnte jeg ikke noget om java. Når det så er sagt, så er der ikke noget i sproget java, som gør at det ikke lige så godt kunne compileres ned og køre på "resourceknappe platforme". Java kunne såmænd godt kompileres "ned til jernet" i stedet for at blive kompileret til "hardward-neutral" java-bytekode, hvis man vil undgå den "store tunge" JVM.
Heldigvis findes der andre sprog end Java og C.
SUN's sunspot kører Java VM på "bare metal"
What kind of operating system does the Sun SPOT run on?
There is no operating system used. The Sun SPOT runs a Java VM on the bare metal.
men man kører nu engang ikke Java på en 8-bit microcontroller...
Man "kører" vel heller ikke C på en microcontroller, men man udvikler muligvis i C, for efterfølgende at kompilere til brug på mikrokontolleren.
Det skulle jo gerne fylde så lidt som muligt, men egentlig kunne man vel bruge et hvilket som helst anvendeligt programmeringssprog der kan kompilere til den givne processor - eller man kan jo være hardcore og skrive direkte i assembler.
Herudover forstår jeg slet ikke alt hypen omkring C og alle dets varianter.
/Christian
Der er ikke (længere) nogen "hype" omkring C. Det er en defacto-standard i visse områder af branchen, som en masse udviklere derfor (uden nogen god grund, og heriblandt øjensyneligt Poul-Henning) må lide under. Mange af dem ved heldigvis ikke selv at de lider :-)
Jeg vil holde med PHK, Java er et teenagersprog, hvor man ikke har nogen som helst kontrol over hvad der virkelig sker i de mange modificerede og udokumenterede objekter. Men nu må i også godt kalde mig for en programmerings dinosaur :-D Der synes det eneste 'Bare Metal' sprog er assembler, og C er højniveau ;-)
Jeg bruger selv Codevision C til AVR controllere (Altså ikke PHK's AVR GCC / GNU compiler).
Codevision fungerer absolut strålende! (Men koster 150 Euro).
Korrekt kode er: (Iflg dokumentationen:)
enum months {january, february, march, april, may, june, july, august, september, october, november, december} months_of_the_year;
Var det ikke det du efterlyste PHK ?
Ups jeg overså lige at det muligvis ikke var AVR controllere der var tale om. Variablen Vcore kunne pege på en version af ARM.
Men et opslag i Kernighan and Ritchie (1988) som jeg selvfølgelig har stående i bogreolen .. :-S viser at den korrekte syntax for enum er:
enum months { JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC};
Så meget tæt op af PHK's eget forslag til hvordan det burde være løst. Eettallet ved JAN er for at give den første værdi 1, ellers bliver den defaulted til 0.
Nej, det er ikke hvad jeg efterlyser.
Jeg efterlyser at min array bliver dimensioneret så den kan indexeres af alle definerede elementer i enum'en.
Poul-Henning
Nu er der jo tale om hardwarenært programmering, så jeg er lidt nysgerrig i forhold til Java her: Hvordan angiver man absolutte adresser i Java? Hvordan skifter man endianess i Java? Hvordan mikser man assembler med Java? Hvordan laver man interuptrutiner i Java?
Disclaimer: Jeg forslår ikke at java er det rigtige valg til systemprogrammering. Formålet med dette indlæg er blot at pointere at man godt kan, hvis man vil.
1) absolutte adresse kan angives i en integer. Hukommelse kan projekteres via et array. Arrayet kan oprettes via JNI kald.
2) Det gør man ikke, ligesom der ikke findes nogen måde i C. Det er en opgave for oversætteren.
3) Man bruger JNI.
4) På samme måde som i C. Bemærk at hverken C eller Java har nogen sprogegenskab som er specielt rettet mod at skrive interruptrutiner. Det er op til oversætteren og eksekveringsmiljøet om det smertefrit lader sig gøre.
Bemærk at hverken C eller Java har nogen sprogegenskab som er specielt rettet mod at skrive interruptrutiner
man sigaction ->
"The sigaction() system call is used to change the action taken by a process on receipt of a specific signal."
man sigaction -> "The sigaction() system call is used to change the action taken by a process on receipt of a specific signal."
Det står ellers meget tydeligt i det du citerer: sigaction er et systemkald, og ikke en del af sproget C. Det er f.eks. ikke tilgængeligt på mange platforme som ellers godt kan køre C programmer.
Du falder i standard fælden med at blande OS kald sammen med sproget.
På en mikrokontroller uden OS er der ikke noget sigaction kald - med mindre du skriver det selv i assembler. Og da kan du ligeså godt skrive sigaction på en måde, så den kan finde ud af at kalde en java metode, eller en metode i et vilkårligt andet sprog.
Det er jo et specifikt systemkald. Det er vel også op til eksekveringsmiljøet at bestemme om det virker eller ej.
sigaction() findes ikke på alle platforme.
Du falder i standard fælden med at blande OS kald sammen med sproget.
At jeg nævnte sigaction, har følgende grund: "[..]Avoid its use: use sigaction(2) instead"
Siden C89 har sproget haft signals, men på de fleste, POSIX kompatible, platforme anbefales det at anvende sigaction. "signal - ANSI C signal handling" "CONFORMING TO C89, C99, POSIX.1-2001."
Siden C89 har sproget haft signals
Du mener at standard C biblioteket har haft signals. Det er mig bekendt ikke et reserveret keyword i sproget C. Det er en del af eksekveringsmiljøet, som ofte ikke er fuldt tilgængeligt. Normalt kalder man det vist stadig for C, selv når platformen ikke implementere hele standard C lib.
En java compiler til en mikrokontroller vil naturligvis komme med en tilsvarende funktion. Det er dog ikke en del af standard java biblioteket, for java er ikke tænkt til det brug.
Det var ikke min mening at vi skulle ud i et langt skænderi om hvad der ligger i diverse biblioteker. Mit budskab er simpelt at i selve sproget er der som sådan ikke den store forskel.
Jeg efterlyser at min array bliver dimensioneret så den kan indexeres af alle definerede elementer i enum'en.
Tja, følgende virker i hvertfald under gcc:
enum hold_vars {
TEMPI, TEMPIX, TEMPO, TEMPOX,
PWM, RPM, VCC, VCORE, VREF,
__LAST /* !!! Keep this as last enum at all times !!! */
};
static uint16_t hold[__LAST];
Hvis jeg husker rigtigt er det altid (signed?) int's der gemmer sig bag enums. De int værdier de antager ramper op, 0-baseret - når intet andet er angivet.
Men det gælder måske ikke for din µC compiler?
Ja, det virker, men forklar mig lige om du synes det er kønt at du skal bruge seks udråbstegn for at forklare læseren hvad det er du ikke kan forklare C-compileren ?
Min anke er ikke at man ikke kan lave et hack, min anke er at det ikke kan laves rent.
Poul-Henning
