####################################### # # # Norme de développement in * # # # # Made by Nicolas Sadirac # # Login rn@epita.fr # # # ####################################### Version du 11 octobre 2000 04h45. Cette norme a pour but d'uniformiser l'écriture des programmes au sein d'EPITA et d'EPITECH. Elle concerne: 1) La nomination des objets 2) La présentation globale (paragraphes) 3) La présentation locale (lignes) 4) Headers (fichier includes) 5) Des recommandations (non obligatoires) 6) Un exemple de Makefile 1) La nomination des objets : Les objets (variable, fonctions, macros, types, fichiers ou répertoires) doivent avoir des noms les plus explicites ou mnémoniques. Les abréviations sont TOLEREES dans la mesure ou elles permettent de réduire significativement la taille du nom sans en perdre le sens. Les parties des noms composites serons séparées par '_'. ex: #define MAX_LINE_SIZE 256 #define COMMENT_CHAR '#' #define MAX_ANIM_NAME_LENGTH 64 #define MAX_FILE_NAME_LENGTH 1024 * Tous les identifiants (fonctions, macros, types, variables etc.) doivent être en anglais. * Les noms de variables et de fonctions de fichiers et de répertoires doivent être composes exclusivement de minuscules, de chiffres et de '_'. * Les noms de macros doivent être en majuscules. Leurs paramètres ont des noms comme `Ceci', pas comme `CA', et pas plus `ainsi'. # define XFREE(Var) \ do \ { \ if (Var) \ free(Var); \ } \ while (0) * Dans le cas de la déclaration d'une structure le nom commencera par "s_", pour un type par "t_", pour une union par "u_" et pour une enum "e_". ex: typedef unsigned char t_cmap[CMAP_NBR_COL*CMAP_SIZE]; typedef unsigned char t_pixel; struct s_picture { int width; int height; t_cmap cmap; t_pixel *picture; }; typedef struct s_picture t_picture; ATTENTION on peut avoir aussi : typedef struct s_picture { int width; int height; t_cmap cmap; t_pixel *picture; } t_picture; * Les noms de globales (si il doit absolument y en avoir) commenceront par "gl_" 2) Présentation globale * L'indentation sera celle obtenue avec Emacs (avec la config dans /u/a1/.env/.emacs) int get_type(char type_char) { t_types *types; for (types = types_tab; types->type_val; types++) if (types->type_val == type_char) return (types->type_val); return (FALSE); } * On passe toujours a la ligne après '{', '}' ou une structure de contrôle. On indente une première fois pour les accolades, puis une seconde pour leur contenu. if (cp) return (cp); /* Incorrect */ if (cp) {return (cp);} /* Incorrect */ if (cp) /* Admis */ { return (cp); } if (cp) /* Correct */ { return (cp); } Selon sa configuration, Emacs indentera comme dans le troisième ou quatrième exemple. Les deux sont acceptés, le dernier préféré. Pour les mêmes raisons, la taille du saut de l'indentation peut varier. On préfèrera 2 espaces. * On sautera une ligne entre la déclaration de variable et les instructions. On met une variable par ligne. Il ne doit pas y avoir d'autres lignes vides dans les blocs. si vous avez envie de séparer des parties d'un bloc faites soit plusieurs blocs, soit une fonction, soit une macro. t_script *load_script(char *file_name) { FILE *fd; t_script *script_head; t_script *last_script; char line_buff[MAX_LINE_SIZE+2]; int current_line; int nbr_line; current_line = 0; nbr_line = 0; script_head = SCRIPT_END; F_FOPEN(fd, file_name, "r", "script file"); ... * Une ligne ne doit pas exceder 80 caracteres. * On fera attention à avoir des fonctions courtes et claires, de 25 lignes maximum (entre les 2 accolades). On évitera aussi d'avoir plus de 5 fonctions par fichier, une serait l'idéal. * Il ne doit pas y avoir de commentaires dans les fonctions. Si il y a besoin de commentaires ils doivent se trouver avant la la fonction. /* ** cette fonction calcule .... */ void *func_mort_de_rire() { } * Les commentaires sont commencés et terminés par une ligne seule. Toutes les lignes intermédiaires s'alignent sur elles, et commencent par `**' /* ** Correct */ /* * Incorrect */ * La déclaration de paramètre se fera a la syntaxe ISO/ANSI C type func(type1 p1, type2 p2, type3 p3) { } voire type func(type1 p1, type2 p2, type3 p3) { } 3) Présentation locale * Un espace derrière le `;' et derrière la `,' : for (i = 0; i < 255; i++) sum(&acu, i); * Pas d'espace entre le nom d'une fonction et la '(' par contre toujours un espace entre un mot clé C (avec argument) et la '(' script_head = SCRIPT_END; F_FOPEN(fd, file_name, "r", "script file"); while (!feof(fd)) { if (!fgets(line_buff, MAX_LINE_SIZE+1, fd) || *line_buff == COMMENT_CHAR) continue; line_buff[strlen(line_buff) - 1] = END_OF_STRING; #if (DEBUG&D_LOAD) my_printf("got:\"%s\"\n", line_buff); #endif put_in_script(&script_head, &last_script, line_buff); } ... Ceci concerne `for' etc. mais aussi `return' et `sizeof'. * On remarquera que `return' est une structure de contrôle du C, et `exit' une fonction, donc attention aux espaces devant la parenthèse. /* Correct */ if (!cp) exit(1); return (cp) /* Deux fois incorrect */ if (!cp) exit (1); return(cp) Lorsque `return' prend un argument, ce dernier doit etre entre parentheses: /* Correct */ return (0); /* Incorrect */ return 0; * Au maximum on doit trouver 4 paramètres dans une fonction. (La fonction qui possédera au maximum 4 paramètres aura une vitesse exécution plus rapide que celle qui en passera plus). Pour passer plus d'information faites une structure et passez un pointeur sur cette structure. * Le symbole de pointeur (*) porte toujours sur la variable (ou fonction), et jamais sur le type: char *cp; /* Correct */ char* cp; /* Incorrect */ * On alignera les déclarations avec des tab (sous Emacs M-I) type v1; struct s_toto *first_toto; struct s_toto my_struct_toto; int *tmp_int; * Lorsqu'on déclare une variable on ne peut affecter en même temps une valeur excepte lorsqu'on utilise une variable static. int bidon = 1; /* Incorrect */ static int pipot = 1; /* Correct */ int dum; /* Correct */ * Affectations : il doit y avoir un espace entre la variable gauche et le signe d'affectation, de même pour la variable a droite. var1 = var2 var1 += var2 var1 *= var2 * Opérateurs unaires. Pas d'espace. *cp &cp -n * Opérateurs bi/ternaires. Tous les opérateurs binaires et ternaires sont séparés des arguments par un espace de part et d'autre. if (a % 10) return (cp ? cp : DEFAULT_STRING) * Les #if et #ifdef indentent d'un caractère les directives cpp qui suivent. Les #else et #endif marquent les conditions dont ils sont issus. #ifndef DEV_BSIZE # ifdef BSIZE # define DEV_BSIZE BSIZE # else /* !BSIZE */ # define DEV_BSIZE 4096 # endif /* !BSIZE */ #endif /* !DEV_BSIZE */ 4) Mots interdits * Vous n'avez pas droit au switch. 5) Headers * La norme dans son intégralité est valide pour les headers. * On les protégera contre double inclusion. Si le fichier est foo.h, la macro témoin est FOO_H_ #ifndef FOO_H_ # define FOO_H_ /* ** Content of foo.h */ #endif /* !FOO_H_ */ * Toutes les fonctions, variables etc. exportées par foo.c sont prototypées dans foo.h. * Les fonctions et variables non exportées par foo.c sont déclarées static. * foo.c inclue toujours foo.h. * Les headers ne doivent pas faire d'include eux-mêmes. A l'extrême limite on pourra inclure des headers non systèmes, mais *jamais* d'inclusion de headers systèmes. 6) Recommandations Le suivi de ces recommandations est conseillé, mais pas obligatoire. * Certaines fonctions de la bibliothèque C peuvent retourner une valeur particulière lorsqu'elles échouent. Le code principal ne doit pas être polué par le traitement de ces valeurs exceptionnelles, aussi faut-il introduire des versions de ces fonctions qui traitent les erreurs elles-mêmes par un appel à exit. Ces fonctions doivent porter le nom de la fonction de la lib C, préfixé par "x". void *xmalloc(size_t n) { void *p; p = malloc(n); if (p == 0) { fprintf(stderr, "Virtual memory exhausted\n"); exit(1); } return (p); } * Il est recommandé de commenter en anglais. 7) Un exemple de Makefile : On prendra soin de toujours compiler avec les options d'optimisation (-g). # # Makefile for PCE in play # # Made by zool # Login# # Started on Sat Oct 16 11:41:48 1993 zool ## Last update Fri Oct 20 10:35:30 2000 assistant c unix # CC = cc YACC = yacc -d COMP = gzip -f UNCOMP = gunzip -f RM = rm -f STRIP = echo strip # NAME = play IPATH = -I. -I../include OPTI = -g #-O2 LPATH = -L. # CFLAGS = $(OPTI) $(IPATH) LDFLAGS = $(OPTI) $(LPATH) LIBS = # # L_SRC = main.c load_script.c send_it.c play_it.c proto_init.c\ proto_send.c proto_pict.c L_OBJS = main.o load_script.o send_it.o play_it.o proto_init.o \ proto_send.o proto_pict.o INCL = gen.h ../include/spe.h config.h\ local_func.h play.h proto.h SRC = $(L_SRC) OBJS = $(L_OBJS) # # # all : $(NAME) $(NAME) : $(OBJS) $(CC) $(LDFLAGS) -o $(NAME) $(OBJS) $(LIBS) $(STRIP) $(NAME) re : clean all clean : -$(RM) $(OBJS) -$(RM) *~ -$(RM) \#* fclean : clean -$(RM) $(NAME) comp : fclean $(COMP) $(SRC) $(INCL) ucomp : $(UNCOMP) $(SRC) $(INCL) .y.o : $(YACC) $< $(CC) -c $(CFLAGS) y.tab.c -@$(RM) y.tab.c mv y.tab.o $*.o mv y.tab.h $*.h .SUFFIXES: .h.Z .c.Z .y.Z .l.Z .x.Z .x\ .c.gz .h.gz .l.gz .y.gz .x.gz \ .x.z .c.z .h.z .l.z .y.z .c.Z.c .h.Z.h .l.Z.l .y.Z.y .x.Z.x \ c.gz.c .h.gz.h .l.gz.l y.gz.y .x.gz.x \ .x.z.x .c.z.c .h.z.h .l.z.l .y.z.y : -$(UNCOMP) $< .x.h : rpcgen $*.x @-$(RM) rnusers_svc.c .c.o : $(CC) $(CFLAGS) -c $< # # deps # $(OBJS) : $(INCL) main.o : main.c load_script.o : load_script.c send_it.o : send_it.c play_it.o : play_it.c proto_init.o : proto_init.c proto_send.o : proto_send.c proto_pict.o : proto_pict.c