08 advanced techniques
DESCRIPTION
hihiTRANSCRIPT
11
AdvancedAdvancedTechniquesTechniques
C F
ou
nd
atio
nC
Fo
un
dat
ion
22cl
ean
Ccl
ean
C• code that compiles in both C and C++code that compiles in both C and C++
C++ compiler is stricter than CC++ compiler is stricter than C e.g. pre C99 did not require function declarationse.g. pre C99 did not require function declarations e.g. C++ requires more conversions to be explicite.g. C++ requires more conversions to be explicit avoid C++ keywordsavoid C++ keywords
boolcatchclassconst_castdelete dynamic_castexplicitexportfalsefriendmutablenamespace
C++ keywords that are not also C keywordsC++ keywords that are not also C keywords
newoperatorprivateprotectedpublicreinterpret_caststatic_casttemplatethis throwtruetry
typeidtypenameusingvirtualwchar_t
33co
mm
ents
com
men
ts• Comments can indicate missing codeComments can indicate missing code
void test_is_leap_year(void){ // years not divisible by 4 are leap years assert(is_leap_year(1906)); assert(!is_leap_year(2009));
// years divisible by 4 but not 100 are leap years assert(is_leap_year(1984)); assert(is_leap_year(2008));
// years divisible by 100 but not 400 are not leap years assert(!is_leap_year(1900)); assert(!is_leap_year(2100));
// years divisible by 400 are leap years assert(is_leap_year(2000)); assert(is_leap_year(2400));}
BEFOREBEFORE
44co
mm
ents
com
men
ts• Imagine if C had no commentsImagine if C had no comments
void years_not_divisible_by_4_are_leap_years(void){ assert(is_leap_year(1906)); assert(!is_leap_year(2009));}void years_divisible_by_4_but_not_100_are_leap_years(void){ assert(is_leap_year(1984)); assert(is_leap_year(2008));}void years_divisible_by_100_but_not_400_are_not_leap_years(void){ assert(!is_leap_year(1900)); assert(!is_leap_year(2100));}void years_divisible_by _400_are_leap_years(void){ assert(is_leap_year(2000)); assert(is_leap_year(2400));}
AFTERAFTER
55cl
arit
y ~
bre
vity
clar
ity
~ b
revi
ty• prefer initialization to assignmentprefer initialization to assignment
• don't explicitly compare against true/falsedon't explicitly compare against true/false
int count = 0;
int count;count = 0;
??
if (has_prefix("is") == true) ...
if (has_prefix("is")) ...
??
refactorrefactor
refactorrefactor
66im
plic
it v
s ex
plic
tim
plic
it v
s ex
plic
t• avoid redundant use of true/falseavoid redundant use of true/false
bool is_even(int value){ if (value % 2 == 0) return true; else return false;}
bool is_even(int value){ return value % 2 == 0;}
??
this version is very "solution focused"this version is very "solution focused"
this version is less solution focused;this version is less solution focused;it is more problem focused;it is more problem focused;it is more "declarative"it is more "declarative"
refactorrefactor
77im
plic
it v
s ex
plic
tim
plic
it v
s ex
plic
t• make inter-statement dependencies explicitmake inter-statement dependencies explicit
bool some_func(int value){ if (value % 2 == 0) return alpha(); return beta();}
bool some_func(int value){ if (value % 2 == 0) return alpha(); else return beta();}
??
consider if you accidentally refactor the if without consider if you accidentally refactor the if without the last return - oopsthe last return - oops
the return statements are now at thethe return statements are now at thesame indentation. logical == physicalsame indentation. logical == physical
refactorrefactor
88lo
gic
vs
con
tro
llo
gic
vs
con
tro
l• in general, beware of boolean literalsin general, beware of boolean literals
if (oldest) if (last_access < cut_off) return true; else return false; else return false;
??
again, wordy, verboseagain, wordy, verbose
refactorrefactor
return oldest && last_access < cut_off;
much simpler, reads wellmuch simpler, reads well
99h
ow
to
iter
ate
ho
w t
o it
erat
e• iteration is a common bug hotspotiteration is a common bug hotspot
looping is easy, knowing when to stop is tricky!looping is easy, knowing when to stop is tricky!
• follow the fundamental rule of designfollow the fundamental rule of design always design a thing by considering it in its next always design a thing by considering it in its next
largest contextlargest context what do you want to be true what do you want to be true afterafter the iteration? the iteration? this forms the termination conditionthis forms the termination condition
... search(size_t end, int values[], int find){ size_t at = 0;
// values[at==0 at==end] iteration
at == end || values[at] == find
...}
we didn't find it............we didn't find it............ ....................we found it....................we found it oror(short-circuiting)(short-circuiting)
1010h
ow
to
iter
ate
ho
w t
o it
erat
e• the negation of the termination condition is the negation of the termination condition is
the iteration's continuation conditionthe iteration's continuation condition !(at == end || values[at] == find)!(at == end || values[at] == find) at != end && values[at] != findat != end && values[at] != find
• then simply fill in the loop bodythen simply fill in the loop body
... search(size_t end, int values[], int find){ size_t at = 0;
while (at != end && values[at] != find) { ... } ...}
equivalentequivalent(DeMorgan's Law)(DeMorgan's Law)
at++;
short-circuiting protects values[at] short-circuiting protects values[at]
1111b
ad t
yped
efb
ad t
yped
ef• don't attempt to hide a pointer in a typedefdon't attempt to hide a pointer in a typedef
if it's a pointer make it look like a pointerif it's a pointer make it look like a pointer abstraction is about hiding abstraction is about hiding unimportantunimportant details details
typedef struct date * date;
bool date_equal(const date lhs, const date rhs);
??date.hdate.h
bool date_equal(const struct date * lhs, const struct date * rhs);
what is const?what is const?
bool date_equal(struct date * const lhs, struct date * const rhs);
is it the date object pointed to?is it the date object pointed to?
or is it the pointer?or is it the pointer?
1212g
oo
d t
yped
efg
oo
d t
yped
ef
typedef struct date date;
bool date_equal(const date * lhs, const date * rhs);
date.hdate.h
struct date;
bool date_equal(const struct date * lhs, const struct date * rhs);
date.hdate.halternativesalternatives
no * hereno * here
1313st
ron
g t
yped
efst
ron
g t
yped
ef• a typedef does a typedef does notnot create a new type create a new type
• consider using a wrapper type instead…consider using a wrapper type instead…
typedef struct { int value; } mile;typedef struct { int value; } kilometer;
typedef int mile;typedef int kilometer;
void strong(mile lhs, kilometer rhs){ lhs = rhs;}
void weak(mile lhs, kilometer rhs){ lhs = rhs; ...}
1414w
eak
enu
mw
eak
enu
m• enums are very weakly typedenums are very weakly typed
an enum's enumerators are of type integer, not of an enum's enumerators are of type integer, not of the enum type itself!the enum type itself!
typedef enum { clubs, diamonds, hearts, spades } suit;
typedef enum { spring, summer, autumn, winter} season;
void weak(void){ suit trumps = winter;}
1515st
ron
ger
en
um
sst
ron
ger
en
um
stypedef struct { int value; } suit;extern const suit clubs, diamonds, hearts, spades;
const suit clubs = { 0 }, diamonds = { 1 }, hearts = { 2 }, spades = { 3 };
typedef struct { int value; } season;extern const season spring, summer, autumn, winter;
void strong(void){ suit trumps = winter;}
season.hseason.h
suit.hsuit.h
const season spring = { 0 }, summer = { 1 }, autumn = { 2 }, winter = { 3 };
season.cseason.c
suit.csuit.c
1616si
de
effe
cts
sid
e ef
fect
s• 5.1.2.3 Program semantics5.1.2.3 Program semantics
At certain specified points in the execution At certain specified points in the execution sequence called sequence points, sequence called sequence points, all all side effectsside effects of previous evaluations shall be of previous evaluations shall be
complete and complete and no no side effectsside effects of subsequent evaluations of subsequent evaluations
shall have taken placeshall have taken place
• what constitutes a side effect?what constitutes a side effect? accessing a volatile objectaccessing a volatile object modifying an objectmodifying an object modifying a filemodifying a file calling a function that does any of thesecalling a function that does any of these
1717vo
lati
levo
lati
le• 6.7.3 Type qualifiers6.7.3 Type qualifiers
An object that has volatile-qualified type may be An object that has volatile-qualified type may be modified in ways unknown to the implementation modified in ways unknown to the implementation or have other unknown side effects. Therefore or have other unknown side effects. Therefore any expression referring to such an object shall any expression referring to such an object shall be evaluated strictly according to the rules…be evaluated strictly according to the rules…described in 5.1.2.3described in 5.1.2.3
int global;volatile int reg;... reg *= 1;
reg = global; reg = global;
int v1 = reg; int v2 = reg;...
reg looks unchanged but reg is reg looks unchanged but reg is volatile so an access to the object volatile so an access to the object is required. This access may cause is required. This access may cause its value to change.its value to change.
these cannot be optimized to athese cannot be optimized to asingle assignment.single assignment.
v1 might not equal v2.v1 might not equal v2.
1818ex
erci
seex
erci
se
volatile int m;
void eg(void){ int value = m + m; ...}
• in this statement… where are the sequence points? where are the side-effects? is it undefined?
1919d
epen
den
cies
dep
end
enci
es• #include dependencies are #include dependencies are transitivetransitive
if you change a .h file you have to recompile all if you change a .h file you have to recompile all files that #include it at any depthfiles that #include it at any depth
a visible reflection of the physical couplinga visible reflection of the physical coupling
#include "grommit.h"#include "flange.h"...typedef struct{ grommit w; flange f; ...} wibble_t;
#include "sink.h"#include "washer.h"...typedef struct{ sink dest; washer w; ...} grommit;
wibble_t.hwibble_t.h
grommit.hgrommit.h
sink.hsink.h
washer.hwasher.h
flange.hflange.h
2020o
paq
ue
typ
eso
paq
ue
typ
es• an ADT implementation techniquean ADT implementation technique
a forward declaration gives the name of a typea forward declaration gives the name of a type the definition of the type – and it's accompanying the definition of the type – and it's accompanying
#includes – are #includes – are notnot specified in the header specified in the header all use of the type has to be as a pointer and all all use of the type has to be as a pointer and all
use of the pointer variable has to be via a functionuse of the pointer variable has to be via a function
...typedef struct wibble_tag wibble;
wibble * wopen(const char * filename);
int wclose(wibble * stream);...
wibble.hwibble.h
allall uses of wibble uses of wibblehave to be have to be as pointersas pointers
not definednot defined
minimal #includesminimal #includes
2121m
od
ule
: u
sem
od
ule
: u
se• in most APIs the idea that a set of functions in most APIs the idea that a set of functions
are closely related is quite weakly expressedare closely related is quite weakly expressed
• a struct containing function pointers can a struct containing function pointers can express the idea more stronglyexpress the idea more strongly
int main(int argc, char * argv[]){ wibble * w = wibbles.open(argv[1]); ... wibbles.close(w);}
int main(int argc, char * argv[]){ wibble * w = wopen(argv[1]); ... wclose(w);}
2222m
od
ule
: h
ead
erm
od
ule
: h
ead
er#ifndef WIBBLE_INCLUDED#define WIBBLE_INCLUDED......typedef struct wibble_tag wibble;
struct wibble_api{ wibble * (*open )(const char *); ... int (*close)(wibble *); };extern const struct wibble_api wibbles;
#endif
wibble.hwibble.h
2323m
od
ule
: s
ou
rce
mo
du
le :
so
urc
e#include "wibble.h"......static wibble * open(const char * name){ ...}...
wibble.cwibble.c
static int close(wibble * stream) { ...};
const struct wibble_api wibbles = { open, ..., close};
no need to writeno need to write&open, &close&open, &close
static linkagestatic linkage
2424A
DT
AD
T• opaque type memory management…opaque type memory management…
clients cannot create objects since they don't clients cannot create objects since they don't know how many bytes they occupyknow how many bytes they occupy
#include "wibble.h"
void client(void){ wibble * pointer; ... wibble value; ... ptr = malloc(sizeof(*ptr));}
...typedef struct wibble wibble;...
wibble.hwibble.h
2525sh
ado
w d
ata
typ
esh
ado
w d
ata
typ
e• an ADT can declare its size!an ADT can declare its size!
clients can now allocate the memoryclients can now allocate the memory true representation remains abstracttrue representation remains abstract
typedef struct wibble{ unsigned char size[16];} wibble;
bool wopen(wibble *, const char *);void wclose(wibble *);
wibble.hwibble.h
#include "wibble.h"void client(const char * name){ wibble w; if (wopen(&w, name)) ... wclose(&w);}
2626sh
ado
wed
typ
esh
ado
wed
typ
e• implementation needs to…implementation needs to…
define the true representation typedefine the true representation type
wibble.cwibble.c
#include "grommit.h"#include "flange.h"
typedef struct{ grommit g; flange f; ...} wibble_rep;
typedef struct wibble { unsigned char size[16];} wibble;
wibble.hwibble.h
the analogy is that thethe analogy is that thetrue type caststrue type castsa shadow which a shadow which reveals only its sizereveals only its size
Still some problemsStill some problemsthough…though…
2727p
rob
lem
1p
rob
lem
1• the size of the type must not be smaller than the size of the type must not be smaller than
the size of the type it shadowsthe size of the type it shadows assert only works at runtimeassert only works at runtime it would be safer to check at compile timeit would be safer to check at compile time
typedef struct wibble { ...} wibble;
wibble.hwibble.h
typedef struct{ ...} wibble_rep;
wibble.cwibble.c
assert(sizeof(wibble) >= sizeof(wibble_rep))
2828d
eep
mag
icd
eep
mag
ic• use a compile time assertionuse a compile time assertion
you cannot declare an array with negative sizeyou cannot declare an array with negative size
#define COMPILE_TIME_ASSERT(desc,exp) \¬ extern char desc [ (exp) ? 1 : -1 ]
compile_time_assert.hcompile_time_assert.h
#include "compile_time_assert.h"COMPILE_TIME_ASSERT(ok, 1 == 1);
#include "compile_time_assert.h"COMPILE_TIME_ASSERT( your_message, 1 == 0);
gcc error: size of array 'your_message' is negative
2929co
mp
atib
le s
ize
com
pat
ible
siz
e#include "wibble.h"#include "flange.h"#include "grommet.h"#include "compile_time_assert.h"
typedef struct{ grommet g; flange f;} wibble_rep;
COMPILE_TIME_ASSERT( sizeof_wibble_not_less_than_sizeof_wibble_rep, sizeof(wibble) >= sizeof(wibble_rep));...
wibble.cwibble.c
3030p
rob
lem
2p
rob
lem
2• the two types must be alignment compatiblethe two types must be alignment compatible
this means the first member of both types must be this means the first member of both types must be alignment compatiblealignment compatible
typedef struct{ grommit g; flange f; ...} wibble_rep;
wibble.cwibble.c
typedef struct wibble { unsigned char size[16];} wibble;
wibble.hwibble.h
3131al
ign
men
t so
luti
on
alig
nm
ent
solu
tio
n• use a union to force alignmentuse a union to force alignment
typedef union{ char c,*cp; int i,*ip; long l,*lp; long long ll,*llp; float f,*fp; double d,*dp; long double ld,*ldp; void *vp; void (*fv)(void); void (*fo)(); void (*fe)(int,...);} alignment;
#include "alignment.h"...typedef union wibble{ alignment universal; unsigned char size[16];} wibble;...
alignment.halignment.h
wibble.hwibble.h
List all the primitive types in here. List all the primitive types in here. One must have the strictest alignmentOne must have the strictest alignment
3232p
rob
lem
3p
rob
lem
3• unions are much rarer than structsunions are much rarer than structs
design is as much about design is as much about useuse as implementation as implementation
6.7.2.3 Tags6.7.2.3 TagsParagraph 1, sentence 2Paragraph 1, sentence 2Where two declarators that use the same tag declare Where two declarators that use the same tag declare the same type, they the same type, they shallshall both use the same choice both use the same choice of struct, union, or enum.of struct, union, or enum.
typedef struct wibble wibble;...
client.hclient.hforward declarationforward declaration
Oooops: it's a union not a structOooops: it's a union not a struct
3333w
rap
per
str
uct
wra
pp
er s
tru
ct• put a union object inside a struct!put a union object inside a struct!
drop the union tag namedrop the union tag name
6.7.2.3 Tags6.7.2.3 TagsParagraph 4, sentence 2Paragraph 4, sentence 2Each declaration of a structure, union, or Each declaration of a structure, union, or enumerated type which does not include a tag enumerated type which does not include a tag declares a distinct typedeclares a distinct type
...typedef struct wibble { union { alignment universal; unsigned char size[16]; } shadow;} wibble;...
wibble.hwibble.h
one objectone object
3434co
nve
rsio
ns
1co
nve
rsio
ns
1• conversion can be via memcpyconversion can be via memcpy
helper function allows assignmenthelper function allows assignment
wibble.cwibble.c
wibble.cwibble.c
static inline wibble shadow(wibble_rep * src){ wibble dst; memcpy(&dst, src, sizeof(*src)); return dst;}
bool wopen(wibble * w, const char * name){ wibble_rep rep = { .g = ..., .f = ..., }; *w = shadow(&rep); ...;}
3535co
nve
rsio
ns
2co
nve
rsio
ns
2• conversion can be via pointer castsconversion can be via pointer casts
helper function allows helper function allows operator operator
wibble.cwibble.c
static inline wibble_rep * rep(wibble * w){ return (wibble_rep *)w;}
void wclose(wibble * w);{ rep(w)->g = ...; rep(w)->f = ...; ...}
3636C
on
tact
Co
nta
ct• This course was written byThis course was written by
Jon Jagger
Expertise: Agility, Process, OO, Patterns
Training+Designing+Consulting+Mentoring
www.jaggersoft.com
jon@ jaggersoft.com
{ JSL }