You are on page 1of 14

CS106L

Spring 2009
Handout #08
April 16, 2009
Advanced Preprocessor Techniques
_________________________________________________________________________________________________________
Introduction
The previous handout on the preprocessor ended on a rather gri note, giving an e!aple o" preprocessor
usage gone a#r$% &ut to entirel$ esche# the preprocessor in "avor o" other language "eatures #ould also
'e an error% (n several circustances, the preprocessor can per"or tas)s that other C** language
"eatures cannot accoplish% This handout e!plores several areas #here the preprocessor can 'e an
invalua'le tool "or solving pro'les and points out several strengths and #ea)nesses o" preprocessor+
'ased approaches%
Special Preprocessor Values
The preprocessor has access to several special values that contain in"oration a'out the state o" the "ile
currentl$ 'eing copiled% The values act li)e #defined constants in that the$ are replaced #henever
encountered in a progra% ,or e!aple, the values __DATE__ and __TIME__ contain string
representations o" the date and tie that the progra #as copiled% Thus, $ou can #rite an
autoaticall$+generated -a'out this progra. "unction using s$nta! siilar to this/
string GetAboutInformation()
{
stringstream result;
result << "Tis !rogram "as #om!iled on " << __DATE__;
result << " at time " << __TIME__;
return result$str();
%
Siilarl$, there are t#o other values, __&I'E__ and __(I&E__, #hich contain the current line nu'er
and the nae o" the "ile 'eing copiled% 0e1ll see an e!aple o" #here __&I'E__ and __(I&E__ can
'e use"ul later in this handout%
String Manipulation Functions
0hile o"ten dangerous, there are ties #here acros can 'e ore po#er"ul or ore use"ul than regular
C** "unctions% Since acros #or) #ith source+level te!t strings instead o" at the C** language level,
soe pieces o" in"oration are availa'le to acros that are not accessi'le using other C** techni2ues%
,or e!aple, let1s return to the MA) acro #e used in the previous handout/
#define MA)(a* b) ((a) + (b) , (a) - (b))
Here, the arguents a and b to MA) are passed '$ string 3 that is, the arguents are passed as the strings
that copose the% ,or e!aple, MA)(./* .0) passes in the value ./ not as a nueric value ten, 'ut as
the character . "ollo#ed '$ the character /% The preprocessor provides t#o di""erent operators "or
anipulating the strings passed in as paraeters% ,irst is the stringizing operator, represented '$ the #
s$'ol, #hich returns a 2uoted, C string representation o" the paraeter% ,or e!aple, consider the
"ollo#ing acro/
+ 1 +
#define 12I'T34T(n) #out << #n << " as 5alue " << (n) << endl
Here, #e ta)e in a single paraeter, n% 0e then use the stringi4ing operator to print out a string
representation o" n, "ollo#ed '$ the value o" the e!pression n% ,or e!aple, given the "ollo#ing code
snippet/
int 6 7 .89;
12I'T34T(6 : ;<);
A"ter preprocessing, this $ields the C** code
int 6 7 .89;
#out << "6 : ;<" << " as 5alue " << (6 : ;<) << endl;
5ote that a"ter the a'ove progra has 'een copiled "ro C** to achine code, an$ notions o" the
original varia'le 6 or the individual e!pressions a)ing up the progra #ill have 'een copletel$
eliinated, since varia'les e!ist onl$ at the C** level% Ho#ever, through the stringi4ing operator, it is
possi'le to preserve a string version o" portions o" the C** source code in the "inal progra, as
deonstrated a'ove% This is use"ul #hen #riting diagnostic "unctions, as $ou1ll see later in this handout%
The second preprocessor string anipulation operator is the string concatenation operator, also )no#n as
the token-pasting operator% This operator, represented '$ ##, ta)es the string value o" a paraeter and
concatenates it #ith another string% ,or e!aple, consider the "ollo#ing acro/
#define DE=&A2E_M>_?A2(t@!e) t@!e m@_##t@!e
The purpose o" this acro is to allo# the user to speci"$ a t$pe 6"or e!aple, int7, and to autoaticall$
generate a varia'le declaration o" that t$pe #hose nae is m@_type, #here type is the paraeter t$pe.
Here, #e use the ## operator to ta)e the nae o" the t$pe and concatenate it #ith the string m@_% Thus,
given the "ollo#ing acro call/
DE=&A2E_M>_?A2(int);
The preprocessor #ould replace it #ith the code
int m@_int;
5ote that #hen #or)ing #ith the to)en+pasting operator, i" the result o" the concatenation does not "or a
single C** to)en 6a valid operator or nae7, the 'ehavior is unde"ined% ,or e!aple, calling
DE=&A2E_M>_?A2(#onst int) #ill have unde"ined 'ehavior, since concatenating the strings m@_ and
#onst int does not $ield a single string 6the result is #onst int m@_#onst int7%
Practical Applications of the Preprocessor I: Diagnostic Debugging Functions
0hen #riting a progra, at ties $ou a$ #ant to ensure that certain invariants a'out $our progra hold
true 3 "or e!aple, that certain pointers cannot 'e '4&&, that a value is al#a$s less than soe constant,
etc% 0hile in an$ cases these conditions should 'e chec)ed using a language "eature called exception
handling, in several cases it is accepta'le to chec) the at runtie using a standard li'rar$ acro called
assert% assert, e!ported '$ the header <#assert+, is a acro that chec)s to see that soe condition
holds true% (" so, the acro has no e""ect% 8ther#ise, it prints out the stateent that did not evaluate to
true, along #ith the "ile and line nu'er in #hich it #as #ritten, then terinates the progra% ,or
e!aple, consider the "ollo#ing code/
+ 2 +
5oid M@(un#tion(int :m@1tr)
{
assert(m@1tr A7 '4&&);
:m@1tr 7 .89;
%
(" a caller passes a null pointer into M@(un#tion, the assert stateent #ill halt the progra and print
out a essage that ight loo) soething li)e this/
Assertion (ailed- Bm@1tr A7 '4&&B- (ile- main$#!!* &ine- ;<
&ecause assert a'ruptl$ terinates the progra #ithout giving the rest o" the application a chance to
respond, $ou should not use assert as a general+purpose error+handling routine% (n practical so"t#are
developent, assert is usuall$ used to e!press prograer assuptions a'out the state o" e!ecution%
,or e!aple, assuing #e have soe enuerated t$pe =olor, suppose #e #ant to #rite a "unction that
returns #hether a color is a priar$ color% Here1s one possi'le ipleentation/
bool Is1rimar@=olor(=olor #)
{
s"it#(#)
{
#ase 2ed-
#ase Green-
#ase Clue-
return true;
default-
D: 3ter"ise* must not be a !rimar@ #olor$ :D
return false;
%
%
Here, i" the color is 2ed, Green, or Clue, #e return that the color is indeed a priar$ color% 8ther#ise,
#e return that it is not a priar$ color% Ho#ever, #hat happens i" the paraeter is not a valid =olor,
perhaps i" the call is Is1rimar@=olor(=olor(E.))9 (n this "unction, since #e assue that that the
paraeter is indeed a color, #e ight #ant to indicate that to the progra '$ e!plicitl$ putting in an
assert test% Here1s a odi"ied version o" the "unction, using assert and assuing the e!istence o" a
"unction Is=olor/
bool Is1rimar@=olor(=olor #)
{
assert(Is=olor(#)); DD Fe assume tat tis is reall@ a #olor$
s"it#(#)
{
#ase 2ed-
#ase Green-
#ase Clue-
return true;
default-
D: 3ter"ise* must not be a !rimar@ #olor$ :D
return false;
%
%
+ : +
5o#, i" the caller passes in an invalid =olor, the progra #ill halt #ith an assertion error pointing us to
the line that caused the pro'le% (" #e have a good de'ugger, #e should 'e a'le to "igure out #hich
caller erroneousl$ passed in an invalid =olor and can 'etter reed$ the pro'le% 0ere #e to ignore this
case entirel$, #e ight have considera'l$ ore trou'le de'ugging the error, since #e #ould have no
indication o" #here the pro'le originated%
;ou should not, ho#ever, use assert to chec) that input "ro Get&ine is correctl$+"ored, "or
e!aple, since it a)es "ar ore sense to repropt the user than to terinate the progra%
0hile assert can 'e used to catch a good nu'er o" prograer errors during developent, it has the
un"ortunate side+e""ect o" slo#ing a progra do#n at runtie 'ecause o" the overhead o" the e!tra
chec)ing involved% Conse2uentl$, ost a<or copilers disa'le the assert acro in release or
optii4ed 'uilds% This a$ see dangerous, since it eliinates chec)s "or pro'leatic input, 'ut is
actuall$ not a pro'le 'ecause, in theor$, $ou shouldn1t 'e copiling a release 'uild o" $our progra i"
assert stateents "ail during e!ecution%
=
&ecause assert is entirel$ disa'led in optii4ed 'uilds, $ou
should use assert onl$ to chec) that speci"ic relations hold true, never to chec) the return value o" a
"unction% (" an assert contains a call to a "unction, #hen assert is disa'led in release 'uilds, the
"unction #on1t 'e called, leading to di""erent 'ehavior in de'ug and release 'uilds% This is a persistent
source o" de'ugging headaches%
>sing the tools outlined in this handout, it1s possi'le "or us to #rite our o#n version o" the assert
acro, #hich #e1ll call =G./H&Assert, to see ho# to use the preprocessor to design such utilities% 0e1ll
split the #or) into t#o parts 3 a "unction called Do=G./H&Assert, #hich actuall$ per"ors the testing
and error+printing, and the acro =G./H&Assert, #hich #ill set up the paraeters to this "unction
appropriatel$% The Do=G./H&Assert "unction #ill loo) li)e this/
#in#lude <#stdlib+ DD for abort();
D: Tese !arameters "ill be assumed to be #orre#t$ :D
5oid Do=G./H&Assert(bool in5ariant* string statement* string file* int line)
{
if(Ain5ariant)
{
#err << "=G./H&Assert (ailedA" << endl;
#err << "E6!ression- " << statement << endl;
#err << "(ile- " << file << endl;
#err << "&ine- " << line << endl;
abort(); DD Iuits !rogram and signals error to te 3G$
%
%
This "unction ta)es in the e!pression to evaluate, along #ith a string representation o" that stateent, the
nae o" the "ile it is "ound in, and the line nu'er o" the initial e!pression% (t then chec)s the invariant,
and, i" it "ails, signals an error and 2uits the progra '$ calling abort()% Since these paraeters are
rather 'ul)$, #e1ll hide the 'ehind the scenes '$ #riting the =G./H&Assert acro as "ollo#s/
#define =G./H&Assert(e6!r) Do=G./H&Assert(e6!r* #e6!r* __(I&E__* __&I'E__)
This acro ta)es in a single paraeter, an e!pression e6!r, and passes it in to the Do=G./H&Assert
"unction% To set up the second paraeter to Do=G./H&Assert, #e get a string representation o" the
e!pression using the stringi4ing operator on e6!r% ,inall$, to get the "ile and line nu'ers, #e use the
= (n practice, this isn1t al#a$s the case% &ut it1s still a nice theor$?
+ @ +
special preprocessor s$'ols __(I&E__ and __&I'E__% 5ote that since the acro is e!panded at the
call site, __(I&E__ and __&I'E__ re"er to the "ile and line #here the acro is used, not #here it #as
declared%
To see =G./H&Assert in action, suppose #e a)e the "ollo#ing call to =G./H&Assert in m@file$#!!
at line .89% Aiven this code/
=G./H&Assert(m@1tr A7 '4&&);
The acro e!pands out to
Do=G./H&Assert(m@1tr A7 '4&&* "m@1tr A7 '4&&"* __(I&E__* __&I'E__);
0hich in turn e!pands to
Do=G./H&Assert(m@1tr A7 '4&&* "m@1tr A7 '4&&"* "m@file$#!!"* .89);
0hich is e!actl$ #hat #e #ant%
5o#, suppose that #e1ve used =G./H&Assert throughout a C** progra and have success"ull$
de'ugged an$ o" its parts% (n this case, #e #ant to disa'le =G./H&Assert "or a release 'uild, so that
the "inal progra doesn1t have the overhead o" all the runtie chec)s% To allo# the user to toggle #hether
=G./H&Assert has an$ e""ect, #e1ll let the #define a constant, '3_=G./H&_AGGE2T, that disa'les
the assertion% (" the user does not de"ine '3_=G./H&_AGGE2T, #e1ll use #define to have the
=G./H&Assert acro per"or the runtie chec)s% 8ther#ise, #e1ll have the acro do nothing% This is
easil$ accoplished using #ifndef %%% #else %%% #endif to deterine the 'ehavior o" =G./H&Assert%
This sart version o" =G./H&Assert is sho#n 'elo#/
+ B +
#ifndef '3_=G./H&_AGGE2T DD Enable assertions
#in#lude <#stdlib+ DD for abort();
D: 'ote tat "e define Do=G./H&Assert inside tis blo#J* sin#e if
: te ma#ro is disabled tereBs no reason to lea5e tis fun#tion sitting
: around$
:D
5oid Do=G./H&Assert(bool in5ariant* string statement* string file* int line)
{
if(Ain5ariant)
{
#err << "=G./H&Assert (ailedA" << endl;
#err << "E6!ression- " << statement << endl;
#err << "(ile- " << file << endl;
#err << "&ine- " << line << endl;
abort(); DD Iuits !rogram and signals error to te 3G$
%
%
#define =G./H&Assert(e6!r) Do=G./H&Assert(e6!r* #e6!r* __(I&E__* __&I'E__)
#else DD Disable assertions
D: Define =G./H&Assert as a ma#ro tat e6!ands to noting$ 'o"* if "e #all
: =G./H&Assert in our #ode* it as absolutel@ no effe#t$
:D
#define =G./H&Assert(e6!r) D: noting :D
#endif
Practical Applications of the Preprocessor II: The Macro Tric!
That acros give C** progras access to their o#n source code can 'e used in other #a$s as #ell% 8ne
lesser+)no#n prograing techni2ue is )no#n as the X Macro trick, a #a$ to speci"$ data in one "orat
'ut have it availa'le in several "orats%
&e"ore e!ploring the C Dacro tric), #e need to cover ho# to rede"ine a acro a"ter it has 'een declared%
Eust as $ou can de"ine a acro '$ using #define, $ou can also unde"ine a acro using #undef% The
#undef preprocessor directive ta)es in a s$'ol that has 'een previousl$ #defined and causes the
preprocessor to ignore the earlier de"inition% (" the s$'ol #as not alread$ de"ined, the #undef directive
has no e""ect 'ut is not an error% ,or e!aple, consider the "ollo#ing code snippet/
#define M>_I'T .89
int 6 7 M>_I'T; DD M>_I'T is re!la#ed
#undef M>_I'T;
int M>_I'T 7 ;<; DD M>_I'T not re!la#ed
The preprocessor #ill re#rite this code as
int 6 7 .89;
int M>_I'T 7 ;<;
Although M>_I'T #as once a #defined constant, a"ter encountering the #undef stateent, the
preprocessor stopped treating it as such% Thus, #hen encountering int M>_I'T 7 ;<, the preprocessor
+ 6 +
ade no replaceents and the code copiled as #ritten%
To introduce the C Dacro tric), let1s consider a coon prograing pro'le and see ho# #e should
go a'out solving it% Suppose that #e #ant to #rite a "unction that, given as an arguent an enuerated
t$pe, returns the string representation o" the enuerated value% ,or e!aple, given the enum
enum =olor {2ed* Green* Clue* =@an* Magenta* >ello"%;
0e #ant to #rite a "unction called =olorToGtring that returns a string representation o" the color% ,or
e!aple, passing in the constant 2ed should hand 'ac) the string "2ed", Clue should $ield "Clue", etc%
Since the naes o" enuerated t$pes are lost during copilation, #e #ould norall$ ipleent this
"unction using code siilar to the "ollo#ing/
string =olorToGtring(=olor #)
{
s"it#(#)
{
#ase 2ed- return "2ed";
#ase Clue- return "Clue";
#ase Green- return "Green";
#ase =@an- return "=@an";
#ase Magenta- return "Magenta";
#ase >ello"- return ">ello"";
default- return "<unJno"n+";
%
%
5o#, suppose that #e #ant to #rite a "unction that, given a color, returns the opposite color%
=
0e1d need
another "unction, li)e this one/
=olor Get3!!osite=olor(=olor #)
{
s"it#(#)
{
#ase 2ed- return =@an;
#ase Clue- return >ello";
#ase Green- return Magenta;
#ase =@an- return 2ed;
#ase Magenta- return Green;
#ase >ello"- return Clue;
default- return #; DD 4nJno"n #olor* undefined result
%
%
These t#o "unctions #ill #or) correctl$, and there1s nothing "unctionall$ #rong #ith the as #ritten%
The pro'le, though, is that these "unctions are not scalable% (" #e #ant to introduce ne# colors, sa$,
Fite and Cla#J, #e1d need to change 'oth =olorToGtring and Get3!!osite=olor to incorporate
these ne# colors% (" #e accidentall$ "orget to change one o" the "unctions, the copiler #ill give no
#arning that soething is issing and #e #ill onl$ notice pro'les during de'ugging% The pro'le is
that a color encapsulates ore in"oration than can 'e e!pressed in an enuerated t$pe% Colors also
have naes and opposites, 'ut the C** enum =olor )no#s onl$ a uni2ue (F "or each color and relies on
correct ipleentations o" =olorToGtring and Get3!!osite=olor "or the other t#o% Soeho#,
= ,or the purposes o" this e!aple, #e1ll #or) #ith additive colors% Thus red is the opposite o" c$an, $ello# is the
opposite o" 'lue, etc%
+ G +
#e1d li)e to 'e a'le to group all o" this in"oration into one place% 0hile #e ight 'e a'le to accoplish
this using a set o" C** stru#t constants 6e%g% de"ining a color stru#t and a)ing #onst instances o"
these stru#ts "or each color7, this approach can 'e 'ul)$ and tedious% (nstead, #e1ll choose a di""erent
approach '$ using C Dacros%
The idea 'ehind C Dacros is that #e can store all o" the in"oration needed a'ove inside o" calls to
preprocessor acros% (n the case o" a color, #e need to store a color1s nae and opposite% Thus, let1s
suppose that #e have soe acro called DE(I'E_=3&32 that ta)es in t#o paraeters corresponding to
the nae and opposite color% 0e ne!t create a ne# "ile, #hich #e1ll call #olor$, and "ill it #ith calls to
this DE(I'E_=3&32 acro that e!press all o" the colors #e )no# 6let1s ignore the "act that #e haven1t
actuall$ de"ined DE(I'E_=3&32 $etH #e1ll get there in a oent7% This "ile loo)s li)e this/
File: color.h
DE(I'E_=3&32(2ed* =@an)
DE(I'E_=3&32(=@an* 2ed)
DE(I'E_=3&32(Green* Magenta)
DE(I'E_=3&32(Magenta* Green)
DE(I'E_=3&32(Clue* >ello")
DE(I'E_=3&32(>ello"* Clue)
T#o things a'out this "ile should <up out at $ou% ,irst, #e haven1t surrounded the "ile in the traditional
#ifndef %%% #endif 'oilerplate, so clients can #in#lude this "ile ultiple ties% Second, #e haven1t
provided an ipleentation "or DE(I'E_=3&32, so i" a caller does include this "ile, it #ill cause a
copile+tie error% ,or no#, don1t #orr$ a'out these pro'les 3 $ou1ll see #h$ #e1ve structured the "ile
this #a$ in a oent%
Let1s see ho# #e can use the C Dacro tric) to re#rite Get3!!osite=olor, #hich "or convenience is
reprinted 'elo#/
=olor Get3!!osite=olor(=olor #)
{
s"it#(#)
{
#ase 2ed- return =@an;
#ase Clue- return >ello";
#ase Green- return Magenta;
#ase =@an- return 2ed;
#ase Magenta- return Green;
#ase >ello"- return Clue;
default- return #; DD 4nJno"n #olor* undefined result
%
%
Here, each one o" the #ase la'els in this s#itch stateent is #ritten as soething o" the "or
#ase color- return opposite;
Loo)ing 'ac) at our #olor$ "ile, notice that each DE(I'E_=3&32 acro has the "or
DE(I'E_=3&32(color* opposite)% This suggests that #e could soeho# convert each o" these
DE(I'E_=3&32 stateents into a #ase la'el '$ cra"ting the proper #define% (n our case, #e1d #ant the
#define to a)e the "irst paraeter the arguent o" the #ase la'el and the second paraeter the return
value% 0e can thus #rite this #define as
+ 8 +
#define DE(I'E_=3&32(#olor* o!!osite) #ase #olor- return o!!osite;
Thus, #e can re#rite Get3!!osite=olor using C Dacros as
=olor Get3!!osite=olor(=olor #)
{
s"it#(#)
{
#define DE(I'E_=3&32(#olor* o!!osite) #ase #olor- return o!!osite;
#in#lude "#olor$"
#undef DE(I'E_=3&32
default- return #; DD 4nJno"n #olor* undefined result$
%
%
This is prett$ cr$ptic, so let1s #al) through it one step at a tie% ,irst, let1s siulate the preprocessor '$
replacing the line #in#lude "#olor$" #ith the "ull contents o" #olor$/
=olor Get3!!osite=olor(=olor #)
{
s"it#(#)
{
#define DE(I'E_=3&32(#olor* o!!osite) #ase #olor- return o!!osite;
DE(I'E_=3&32(2ed* =@an)
DE(I'E_=3&32(=@an* 2ed)
DE(I'E_=3&32(Green* Magenta)
DE(I'E_=3&32(Magenta* Green)
DE(I'E_=3&32(Clue* >ello")
DE(I'E_=3&32(>ello"* Clue)
#undef DE(I'E_=3&32
default- return #; DD 4nJno"n #olor* undefined result$
%
%
5o#, #e replace each DE(I'E_=3&32 '$ instantiating the acro, #hich $ields the "ollo#ing/
=olor Get3!!osite=olor(=olor #)
{
s"it#(#)
{
#ase 2ed- return =@an;
#ase Clue- return >ello";
#ase Green- return Magenta;
#ase =@an- return 2ed;
#ase Magenta- return Green;
#ase >ello"- return Clue;
#undef DE(I'E_=3&32
default- return #; DD 4nJno"n #olor* undefined result$
%
%
,inall$, #e #undef the DE(I'E_=3&32 acro, so that the ne!t tie #e need to provide a de"inition "or
DE(I'E_=3&32, #e don1t have to #orr$ a'out con"licts #ith the e!isting declaration% Thus, the "inal
code "or Get3!!osite=olor, a"ter e!panding out the acros, $ields
+ 9 +
=olor Get3!!osite=olor(=olor #)
{
s"it#(#)
{
#ase 2ed- return =@an;
#ase Clue- return >ello";
#ase Green- return Magenta;
#ase =@an- return 2ed;
#ase Magenta- return Green;
#ase >ello"- return Clue;
default- return #; DD 4nJno"n #olor* undefined result$
%
%
0hich is e!actl$ #hat #e #anted%
The "undaental idea underl$ing the C Dacros tric) is that all o" the in"oration #e can possi'l$ need
a'out a color is contained inside o" the "ile #olor$% To a)e that in"oration availa'le to the outside
#orld, #e e'ed all o" this in"oration into calls to soe acro #hose nae and paraeters are )no#n%
0e do not, ho#ever, provide an ipleentation o" this acro inside o" #olor$ 'ecause #e cannot
anticipate ever$ possi'le use o" the in"oration contained in this "ile% (nstead, #e e!pect that i" another
part o" the code #ants to use the in"oration, it #ill provide its o#n ipleentation o" the
DE(I'E_=3&32 acro that e!tracts and "orats the in"oration% The 'asic idio "or accessing the
in"oration "ro these acros loo)s li)e this/
#define macroname(arguments) D: some use for te arguments :D
#in#lude "filename"
#undef macroname
Here, the "irst line de"ines the echanis #e #ill use to e!tract the data "ro the acros% The second
includes the "ile containing the acros, #hich supplies the acro the data it needs to operate% The "inal
step clears the acro so that the in"oration is availa'le to other callers% (" $ou1ll notice, the a'ove
techni2ue "or ipleenting Get3!!osite=olor "ollo#s this pattern precisel$%
0e can also use the a'ove pattern to re#rite the =olorToGtring "unction% 5ote that inside o"
=olorToGtring, #hile #e can ignore the second paraeter to DE(I'E_=3&32, the acro #e de"ine to
e!tract the in"oration still needs to have t#o paraeters% To see ho# to ipleent =olorToGtring,
let1s "irst revisit our original ipleentation/
string =olorToGtring(=olor #)
{
s"it#(#)
{
#ase 2ed- return "2ed";
#ase Clue- return "Clue";
#ase Green- return "Green";
#ase =@an- return "=@an";
#ase Magenta- return "Magenta";
#ase >ello"- return ">ello"";
default- return "<unJno"n+";
%
%
(" $ou1ll notice, each o" the #ase la'els is #ritten as
+ 10 +
#ase color- return "color";
Thus, using C Dacros, #e can #rite =olorToGtring as
string =olorToGtring(=olor #)
{
s"it#(#)
{
D: =on5ert someting of te form DE(I'E_=3&32(#olor* o!!osite)
: into someting of te form B#ase #olor- return "#olor"B;
:D
#define DE(I'E_=3&32(#olor* o!!osite) #ase #olor- return ##olor;
#in#lude "#olor$"
#undef DE(I'E_=3&32
default- return "<unJno"n+";
%
%
(n this particular ipleentation o" DE(I'E_=3&32, #e use the stringi4ing operator to convert the
#olor paraeter into a string "or the return value% 0e1ve used the preprocessor to generate 'oth
Get3!!osite=olor and =olorToGtring?
There is one "inal step #e need to ta)e, and that1s to re#rite the initial enum =olor using the C Dacro
tric)% 8ther#ise, i" #e a)e an$ changes to #olor$, perhaps renaing a color or introducing ne#
colors, the enum #ill not re"lect these changes and ight result in copile+tie errors% Let1s revisit
enum =olor, #hich is reprinted 'elo#/
enum =olor {2ed* Green* Clue* =@an* Magenta* >ello"%;
0hile in the previous e!aples o" =olorToGtring and Get3!!osite=olor there #as a reasona'l$
o'vious apping 'et#een DE(I'E_=3&32 acros and #ase stateents, it is less o'vious ho# to
generate this enum using the C Dacro tric)% Ho#ever, i" #e re#rite this enum as "ollo#s/
enum =olor {
2ed*
Green*
Clue*
=@an*
Magenta*
>ello"
%;
(t should 'e slightl$ easier to see ho# to #rite this enum in ters o" C Dacros% ,or each DE(I'E_=3&32
acro #e provide, #e1ll sipl$ e!tract the "irst paraeter 6the color nae7 and append a coa% (n
code, this loo)s li)e
enum =olor {
#define DE(I'E_=3&32(#olor* o!!osite) #olor* DD 'ame follo"ed b@ #omma
#in#lude "#olor$"
#undef DE(I'E_=3&32
%;
This, in turn, e!pands out to
+ 11 +
enum =olor {
#define DE(I'E_=3&32(#olor* o!!osite) #olor*
DE(I'E_=3&32(2ed* =@an)
DE(I'E_=3&32(=@an* 2ed)
DE(I'E_=3&32(Green* Magenta)
DE(I'E_=3&32(Magenta* Green)
DE(I'E_=3&32(Clue* >ello")
DE(I'E_=3&32(>ello"* Clue)
#undef DE(I'E_=3&32
%;
0hich in turn 'ecoes
enum =olor {
2ed*
Green*
Clue*
=@an*
Magenta*
>ello"*
%;
0hich is e!actl$ #hat #e #ant% ;ou a$ have noticed that there is a trailing coa at a"ter the "inal
color 6>ello"7, 'ut this is not a pro'le 3 it turns out that it1s totall$ legal C** code%
Anal"sis of the Macro Tric!
The C Dacro+generated "unctions have several advantages over the hand+#ritten versions% ,irst, it a)es
the code considera'l$ shorter% &$ rel$ing on the preprocessor to per"or the necessar$ e!pansions, #e
can e!press all o" the necessar$ in"oration "or an o'<ect inside o" an C Dacro "ile and onl$ need to #rite
the s$nta! necessar$ to per"or soe tas) once% Second, and ore iportantl$, this approach eans that
adding or reoving =olor values is siple% 0e sipl$ need to add another DE(I'E_=3&32 de"inition
to #olor$ and the changes #ill autoaticall$ appear in all o" the relevant "unctions% ,inall$, i" #e need
to incorporate ore in"oration into the =olor o'<ect, #e can store that in"oration in one location and
let an$ callers that need it access it #ithout accidentall$ leaving one out%
That said, C Dacros are not a per"ect techni2ue% The s$nta! is considera'l$ tric)ier and denser than in
the original ipleentation, and it1s less clear to an outside reader ho# the code #or)s% Iee'er that
reada'le code is <ust as iportant as correct code, and a)e sure that $ou1ve considered all o" $our
options 'e"ore settling on C Dacros% (" $ou1re ever #or)ing in a group and plan on using the C Dacro
tric), 'e sure that $our other group e'ers are up to speed on the techni2ue and get their approval
'e"ore using it%
=
= The C Dacro tric) is a special case o" a ore general techni2ue )no#n as preprocessor metaprogramming. ("
$ou1re interested in learning ore a'out preprocessor etaprograing, consider loo)ing into the &oost
Detaprograing Li'rar$ 6DJL7, a pro"essional C** pac)age that sipli"ies coon etaprograing tas)s%
+ 12 +
More to #$plore % Practice Proble&s
(1ve co'ined the -Dore to K!plore. and -Jractice Jro'les. section o" this handout 'ecause an$ o"
the topics #e didn1t cover in great detail in this handout are 'est understood '$ pla$ing around #ith the
aterial% Here1s a sapling o" di""erent preprocessor tric)s and techni2ues, i!ed in #ith soe
prograing pu44les/
1% A coon 'ut nonstandard variant o" the assert acro is the 5erif@ acro #hich, li)e
assert, chec)s a condition at runtie and prints and error and terinates i" the condition is
"alse% Ho#ever, in optii4ed 'uilds, 5erif@ is not disa'led, 'ut sipl$ does not a'ort at
runtie i" the condition is "alse% This allo#s $ou to use 5erif@ to chec) the return value o"
"unctions directl$ 6Fo $ou see #h$97% Create a "unction called =G./H&?erif@ that, unless the
s$'ol '3_=G./H&_?E2I(> is de"ined, chec)s the paraeter and a'orts the progra i" it is
"alse% 8ther#ise, i" '3_=G./H&_?E2I(> is de"ined, chec) the condition, 'ut do not terinate
the progra i" it is "alse%
2% Another coon de'ugging acro is a -not reached. acro #hich autoaticall$ terinates the
progra i" e!ecuted% -5ot reached. acros are use"ul inside constructs such as s"it#
stateents #here the default la'el should never 'e encountered% 0rite a acro,
=G./H&'ot2ea#ed, that ta)es in a string paraeter and, i" e!ecuted, prints out the string, "ile
nae, and line nu'er, then calls abort to end the progra% As #ith =G./H&Assert and
=G./H&?erif@, i" the user #defines the s$'ol '3_=G./H&_'3T2EA=KED, change the
'ehavior o" =G./H&'ot2ea#ed so that it has no e""ect%
:% (" $ou1ve done the t#o previous pro'les, $ou1ll notice that #e no# have three constants,
'3_=G./H&_AGGE2T, '3_=G./H&_?E2I(>, and '3_=G./H&_'3T2EA=KED, that all ust 'e
#defined to disa'le the at runtie% This can 'e a hassle and could potentiall$ 'e incorrect i"
#e accidentall$ oit one o" these s$'ols% 0rite a code snippet that chec)s to see i" a s$'ol
naed DIGAC&E_A&&_=G./H&_DEC4G is de"ined and, i" so, disa'les all o" the three
a"oreentioned de'ugging tools% Ho#ever, still give the user the option to selectivel$ disa'le the
individual "unctions%
@% Dodi"$ the earlier de"inition o" enum =olor such that a"ter all o" the colors de"ined in #olor$,
there is a special value, '3T_A_=3&32, that speci"ies a none!istent color% (Hint: Do you actually
need to change color.h to do this!
B% >sing C Dacros, #rite a "unction GtringTo=olor #hich ta)es as a paraeter a string and
returns the =olor o'<ect #hose nae exactly atches the input string% (" there are no colors #ith
that nae, return '3T_A_=3&32 as a sentinel% ,or e!aple, calling
GtringTo=olor("Green") #ould return the value Green, 'ut calling
GtringTo=olor("green") or GtringTo=olor("3li5e") should 'oth return
'3T_A_=3&32%
6% Suppose that $ou #ant to a)e sure that the enuerated values $ou1ve ade "or =olor do not
con"lict #ith other enuerated t$pes that ight 'e introduced into $our progra% Dodi"$ the
earlier de"inition o" DE(I'E_=3&32 used to de"ine enum =olor so that all o" the colors are
pre"aced #ith the identi"ier e=olor_% ,or e!aple, the old value 2ed should change to
e=olor_2ed, the old Clue #ould 'e e=olor_Clue, etc% Fo not change the contents o"
#olor$% (Hint: "se one o# the preprocessor string-manipulation operators!
+ 1: +
G% The #error directive causes a copile+tie error i" the preprocessor encounters it% This a$
sound strange at "irst, 'ut is an e!cellent #a$ "or detecting pro'les during preprocessing that
ight sno#'all into larger pro'les later in the code% ,or e!aple, i" the code uses copiler+
speci"ic "eatures 6such as the 8penDJ li'rar$7, it ight add a chec) to see that a copiler+
speci"ic #define is in place, using #error to report an error i" it isn1t% The s$nta! "or #error
is #error message, #here message is a essage to the user e!plaining the pro'le% Dodi"$
#olor$ so that i" a caller #in#ludes the "ile #ithout "irst #define+ing the DE(I'E_=3&32
acro, the preprocessor reports an error containing a essage a'out ho# to use the "ile%
8% (" $ou1re up "or a challenge, consider the "ollo#ing pro'le% &elo# is a ta'le suari4ing
various units o" length/
'nit (a&e )&eters % unit Suffi$ S"ste&
Deter 1%0 Detric
Centieter 0%01 c Detric
Liloeter 1000%0 ) Detric
,oot 0%:0@8 "t Knglish
(nch 0%02B@ in Knglish
Dile 1609%:@@ i Knglish
Astronoical >nit 1%@96 ! 10
11
A> Astronoical
Light ;ear 9%@61 M 10
1B
l$ Astronoical
Cu'it
=
0%BB cu'it Archaic
a7 Create a "ile called units$ that uses the C acros tric) to encode the a'ove in"oration%
'7 Create an enuerated t$pe, &engt4nit, #hich uses the su""i! o" the unit, preceded '$
e&engt4nit_, as the nae "or the unit% ,or e!aple, a cu'it is e&engt4nit_#ubit,
#hile a ile #ould 'e e&engt4nit_mi% Also de"ine an enuerated value
e&engt4nit_E2232 that serves as a sentinel indicating that the value is invalid%
c7 0rite a "unction called Guffi6GtringTo&engt4nit that accepts a string representation
o" a su""i! and returns the &engt4nit corresponding to that string% (" the string does not
atch the su""i!, return e&engt4nit_E2232%
d7 Create a stru#t, &engt, that stores a double and a &engt4nit% 0rite a "unction
2ead&engt that propts the user "or a double and a string representing an aount and
a unit su""i! and stores data in a &engt% (" the string does not correspond to a su""i!,
repropt the user% ;ou can odi"$ the code "or GetInteger on the CS106L #e'site to
a)e an ipleentation o" Get2eal%
e7 Create a "unction, Get4nitT@!e, that ta)es in a &engt and returns the unit s$ste in
#hich it occurs 6as a string7
"7 Create a "unction, 1rint&engt, that prints out a &engt in the "orat
amount suffix (amount unitnames)% ,or e!aple, i" a &engt stores 10@%2 iles, it
#ould print out ./;$<mi (./;$< Miles)
g7 Create a "unction, =on5ertToMeters, #hich ta)es in a &engt and converts it to an
e2uivalent length in eters%
Surprisingl$, this pro'le is not particularl$ long 3 the ain challenge is the user input, not the
unit anageent?
= There is no agreed+upon standard "or this unit, so this is an appro!iate average o" the various lengths%
+ 1@ +

You might also like