↪ j’étais sur Progdupeupl et encore avant sur le Site du Zéro.
Messages utiles (pour réutilisation future) :
plus quelques trucs moins intéressants là-bas (et voir aussi la bio de realmagma).
À propos de chameaux.
# let default x0 x = let f ?(x=x0) = x in f ?x ;;
val default : 'a -> 'a option -> 'a = <fun>
OCaml, ce langage de script
L’interprète d’OCaml accepte les shebangs, ce qui permet d’écrire de s’en servir comme d’un langage de script :
:::ocaml
#!/usr/bin/ocaml
(* … le code OCaml ici … *)
Par contre, le compilateur (ocamlc
et ocamlopt
) signale une erreur de syntaxe. On peut toutefois leur donner l’option -pp 'sed "1s/^#\!.*//"'
. Elle leur dit d’utiliser la commande fournie comme préprocesseur, et cette commande supprime la ligne du shebang.
Comme pour n’importe quel langage, on peut même utiliser le shebang pour passer des options à l’interprète, par exemple pour charger des modules :
:::ocaml
#!/usr/bin/ocaml str.cma
Problème (non spécifique à OCaml) : beaucoup de systèmes, dont Linux, ne séparent pas les arguments ainsi passés. La ligne suivante passe en fait un seul argument "str.cma unix.cma"
:
:::ocaml
#!/usr/bin/ocaml str.cma unix.cma
Il n’y a pas de solution directe et universelle à ce problème. Une astuce consiste à appeler non pas directement l’interprète, mais le shell (sh
) et à rendre le fichier polyglotte : lu par le shell, il exécutera des instructions du shell (qui consisteront à appeler l’interprète du langage avec les options souhaitées), et lu par l’interprète du langage, il exécutera notre vrai programme. Bien sûr, il faut que les deux programmes soient syntaxiquement corrects et ne fassent que ce qu’on veut… Ci-dessous, deux patrons possibles. Ils utilisent tous deux la plus grande souplesse d’OCaml en ce qui concerne les apostrophes simples, afin d’envelopper le script shell dans un commentaire OCaml sans que les délimiteurs de ces commentaires ne provoquent une erreur de syntaxe pour le shell.
-
Celle-ci utilise la commande true
qui ignore tous ses arguments. Elle induit léger surcoût à l’exécution du code OCaml.
:::ocaml
#!/bin/sh
"true" = let x' = "" in ('
commandes sh ici
) x'
-
Celle-ci utilise la commande type
en ignorant sa sortie. Elle ne provoque aucun surcoût à l’exécution du code OCaml, mais crée un nouveau nom de type (qu’on peut prendre arbitrairement tarabiscoté si c’est gênant).
:::ocaml
#!/bin/sh
type int' (*' >&- 2>&-
commandes sh ici
*)
Dans ces deux patrons, le script shell doit se terminer (par exemple avec un appel à exec
ou exit
sans atteindre la fin du commentaire, sans quoi on obtient une erreur de syntaxe (on peut compliquer ces modèles pour gérer ça, mais ce n’est pas utile pour l’usage qu’on en fait…).
Notez qu’on peut écrire un script shell arbitrairement complexe dans ces patrons (il faut juste éviter la séquence *)
). Personnellement, j’utilise ceci, qui me permet soit d’exécuter le script directement, soit de le compiler en passant l’argument --compile
.
:::ocaml
#!/bin/sh
type int' (*' >&- 2>&-
if [ "$1" = "--compile" ]; then
name="${0%.ml}"
ocamlopt -pp 'sed "1s/^#\!.*//"' \
str.cmxa unix.cmxa "$name.ml" -o "$name" \
|| exit
rm "$name".{cm*,o}
exit
else
exec ocaml str.cma unix.cma "$0" "$@"
fi
*)
Parlons C.
Liens
Les normes : C89, C99, C11.
Deux liens sur l’histoire du C : l’article de Dennis Ritchie, une page instructive.
Aide-mémoire des opérateurs
lignes triées par priorité décroissante :
type |
associativité |
opérateurs |
suffixe |
ltr → |
++ -- . -> [] () (appel de fonction) |
préfixe |
← trl |
++ -- ! ~ + - & * (Type) sizeof _Alignof |
multiplication |
rtl → |
* / % |
addition |
rtl → |
+ - |
décalage binaire |
rtl → |
<< >> |
comparaison |
rtl → |
< <= >= > |
|
|
== != |
bit-à-bit |
rtl → |
& |
|
|
^ |
|
|
¦ |
logique |
rtl → |
&& (court-circuit) |
|
|
¦¦ (court-circuit) |
ternaire |
← rtl |
?: (point de séquence ; expression centrale automatiquement parenthésée, mais pas celle de droite, attention aux affectations) |
affectation |
← rtl |
= += -= *= /= %= <<= >>= &= ^= ¦= |
séquençage |
ltr → |
, (point de séquence) |
(si ça ne vous suffit pas)
Des codes ± utiles rigolos…
Afficher les arguments passés en CLI.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int n, char const* const* a) {
char wd[1024];
if(getcwd(wd, sizeof wd / sizeof *wd) == NULL)
puts("[error when trying to get working directory"
" (function `getcwd`).]");
printf("[current working directory: '%s']\n"
"[%u argument%c passed%c]\n", wd, n, n>1?'s':0, n?':':'.');
for(; *a; ++a)
puts(*a);
return EXIT_SUCCESS;
}
Convertir les tabulations d’un code source en espaces (à chaîner avec xclip
pour le coller sur un site web par exemple).
) (_)) // __
char32_t(_const_const_char )( unsigned char const**const __
//\\
//||\\
// || \\
// || \\
// || \\
// || \\ \_o<
/* || *\
\\ || //
\\ || //
\\ || //
\\ || //
\\ || //
\\||//
\**/
){
;char32_t O
, o ; size_t
l;
for
(l=0,
o=0200,
O=*(*__)++;
o&/*_*/O;++l,
O&=~ o,o>>=1);
(l && (!--l||l&4))
&& (O^=~O); while //&=
(O+ 1&&l --) 0200^0300 &
(o=*(*__)++) &&(O^=~O)||
(O=O <<6 | o&077);return
O;}
Implémentation de printf
(ou une proposition plus sérieuse).
#include <stdarg.h>
#include <stdio.h>
#undef printf
int printf(char const * ____ ,... ) {
va_list ( ___ ) ;
va_start ( ___, ____ ) ;
int va_lid =
vprintf ( ____ ,___ ) ;
va_end ( ___ ) ;
return va_lid ;
}
un quine lisible :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* escape (const char* s)
{
char* u = calloc(2*strlen(s), 1);
for (char* t = u; *s; s++) switch (*s) {
case '\n':
strcpy(t, "\\n\"\n\""); t += 5; break;
case '\"':
*t++ = '\\'; *t++ = '"'; break;
case '\\':
*t++ = '\\'; *t++ = '\\'; break;
default:
*t++ = *s;
}
return u;
}
int main (void)
{
const char* s =
"#include <stdio.h>\n"
"#include <stdlib.h>\n"
"#include <string.h>\n"
"\n"
"char* escape (const char* s)\n"
"{\n"
" char* u = calloc(2*strlen(s), 1);\n"
" \n"
" for (char* t = u; *s; s++) switch (*s) {\n"
" case '\\n':\n"
" strcpy(t, \"\\\\n\\\"\\n\\\"\"); t += 5; break;\n"
" case '\\\"':\n"
" *t++ = '\\\\'; *t++ = '\"'; break;\n"
" case '\\\\':\n"
" *t++ = '\\\\'; *t++ = '\\\\'; break;\n"
" default:\n"
" *t++ = *s;\n"
" }\n"
" \n"
" return u;\n"
"}\n"
"\n"
"int main (void)\n"
"{\n"
" const char* s =\n"
"\"%s\";\n"
" char* t = escape(s);\n"
" printf(s, t);\n"
" free(t);\n"
"}\n"
"";
char* t = escape(s);
printf(s, t);
free(t);
}
un quine court (utilise ça) :
#define _(a,b) a ("_("#a" , "#b")") b
_(int puts(char*); int main (void) { puts("#define _(a,b) a (\"_(\"#a\" , \"#b\")\") b"); puts , ; return 0; })
le même encore plus court, aux dépens de la lisibilité et de la stricte conformité :
#define _(a,b)a("_("#a","#b")")b
_(main(){puts("#define _(a,b)a(\"_(\"#a\",\"#b\")\")b");puts,;})
Des exceptions… en C
Ce code implémente des macros try
et catch
en C. cf. ici. Utilise les sauts non locaux (en-tête <setjmp.h>
). Évidemment à ne pas utiliser dans la vie réelle, pour des raisons de performance (cf. ici pour la culture). On peut vraiment s’amuser avec la syntaxe du C (C99) pour coder des macros. Je devrais enlever tous ces commentaires.
trycatch.h
#ifndef INCLUDED_TRYCATCH_H
#define INCLUDED_TRYCATCH_H
#if !defined (__STDC_VERSION__) || __STDC_VERSION__ < 199901L
#error : USE OF “TRYCATCH.H” REQUIRES C99.
#endif
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
struct try {
struct try *prev;
jmp_buf env;
};
struct catch {
struct try *envs;
int code;
};
extern struct catch catch;
#define Throw(msg) \
fprintf(stderr, "error [file `%s`, line %u, function `%s`]: %s.\n", \
__FILE__, __LINE__, __func__, (msg))
#define try \
for( catch.code = 1, catch.envs = & (struct try) { .prev = catch.envs } \
; catch.code && ( setjmp(catch.envs->env) \
? (catch.envs = catch.envs->prev, 0) \
: 1 ) \
; catch.code = 0, catch.envs = catch.envs->prev \
)
#define catch(VAR) \
for(VAR = catch.code ; catch.code ; catch.code = 0)
#define catchSwitch() \
for(; catch.code ; catch.code = 0) \
switch(catch.code)
#define throw(CODE) \
do { \
catch.code = (CODE); \
if(!catch.code) { \
Throw("throwing a null exception code"); \
exit(EXIT_FAILURE); \
} \
else if(!catch.envs) { \
Throw("`throw` outside of a `try` block"); \
exit(catch.code); \
} \
else \
longjmp(catch.envs->env, catch.code); \
} while(0)
#define rethrow() \
throw(catch.code)
#endif
trycatch.c
#include "trycatch.h"
struct catch catch = {NULL, 0};
test.c
#include "trycatch.h"
#include <stdio.h>
typedef enum {EXC_Z, EXC_NOTRY, EXC_42, EXC_DIVBYZ, EXC_ORDER} Exception;
int division(int a, int b) {
if(!a)
throw(EXC_Z);
else if(a==-1)
throw(EXC_NOTRY);
else if(b==42)
throw(EXC_42);
else if(!b)
throw(EXC_DIVBYZ);
else if(a<b)
throw(EXC_ORDER);
return a / b;
}
int main(void) {
int a, b;
while(1) {
fputs("a, b? ", stdout);
scanf("%i%i", &a, &b);
try {
try {
printf("%i / %i = %i\n", a, b, division(a,b));
}
catchSwitch() {
case EXC_DIVBYZ:
puts("exc: /0"); break;
case EXC_ORDER:
puts("exc: a<b"); break;
default:
rethrow(); break;
}
}
catch(Exception e) {
if(e == EXC_42)
puts("exc: 42");
else
rethrow();
}
}
}
Moi moi moi
Chez moi (<del>mplayer</del> mpv / smplayer ; à tester : gmplayer, gnome-mplayer, dmlenu).