home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 June / Chip_2001-06_cd1.bin / obsahy / Chip_txt / txt / 168-169.txt < prev    next >
Text File  |  2001-04-29  |  12KB  |  243 lines

  1. Jazyk C++
  2. èABLONOV┴ MAGIE
  3. V minul²ch dφlech naÜeho programßtorskΘho serißlku pro pokroΦilΘ jsme vßs seznßmili s metaprogramovßnφm, Üablonov²mi t°φdami rys∙ a Üablonami v²raz∙. Nynφ si na zßv∞r ukß₧eme, jak tyto techniky kombinovat. Pomocφ metafunkcφ budeme definovat t°φdy rys∙, kterΘ popisujφ obvyklΘ aritmetickΘ konverze a celoΦφselnß rozÜφ°enφ p°i vyhodnocovßnφ v²raz∙.
  4.  
  5. SyntΘza typu
  6. AritmetickΘ konverze jsou b∞₧nou souΦßstφ vyΦφslovßnφ aritmetick²ch v²raz∙. Nap°φklad kdy₧ sΦφtßme Φφslo typu double a Φφslo typu int, je v²sledek typu double. Tak je psßno ve standardu jazyka a p°ekladaΦe to zvlßdnou. Pokud ale pou₧ijeme nap°φklad vektor parametrizovan² typem dat
  7. template <class T> class Vektor { /*...*/ };
  8. a chceme seΦtenφm vektor∙ typ∙ Vektor<double> a Vektor<int> dostat v²sledek typu Vektor<double>, nem∙₧eme Φekat pomoc od p°ekladaΦe a musφme si to naprogramovat sami.
  9. ┌kol je tedy jasn² - nauΦit p°ekladaΦ pou₧φvat aritmetickΘ konverze i pro argumenty Üablon. Jak na to? Jednu z mo₧nostφ jsme u₧ naznaΦili v Φlßnku o t°φdßch rys∙ - pro ka₧dou dvojici typ∙ definovat pomocφ explicitnφ specializace typ v²slednΘ hodnoty. Zopakujme si, jak by to vypadalo:
  10. template <class T1, class T2>
  11. struct Vysledek
  12. {
  13. };
  14.  
  15. template <>
  16. struct Vysledek<int, double>
  17. {
  18.    typedef double TYP;
  19. };
  20.  
  21. template <>
  22. struct Vysledek<double, int>
  23. {
  24.    typedef double TYP;
  25. };
  26.  
  27. // ... a tak dßl
  28. Je sice pravda, ₧e poΦet t∞chto kombinacφ je koneΦn², nebo¥ koneΦn² je i poΦet vestav∞n²ch typ∙, ale stßle to p°ipomφnß metodu hrubΘ sφly. Te∩ si ukß₧eme, ₧e to lze zvlßdnout elegantn∞ji, a to pomocφ metafunkcφ. Metafunkce bude mφt dva vstupnφ parametry - typy - a bude vracet v²sledn² typ. Mimo jinΘ p∙jde o p∞kn² p°φklad t°φdy rys∙ definovanΘ pomocφ metafunkce.
  29.  
  30. Jak to vidφ standard
  31. Pro p°ipomenutφ si zde uve∩me pravidla, podle kter²ch se °φdφ aritmetickΘ konverze p°i zpracovßnφ v²raz∙. P°ipome≥me, ₧e nßs zajφmajφ jenom takovΘ situace, kdy parametry operßtor∙ jsou ΦφselnΘ nebo v²ΦtovΘ typy. ZaΦneme u binßrnφch operßtor∙ (standard, Φßst 5.0.9). Nßsledujφcφ pravidla se naz²vajφ obvyklΘ aritmetickΘ konverze:
  32. Jestli₧e jeden z operand∙ je typu long double, pak druh² by m∞l b²t p°eveden na typ long double.
  33. Jinak, jestli₧e jeden z operand∙ je typu double, pak druh² by m∞l b²t p°eveden na typ double.
  34. Jinak, jestli₧e jeden z operand∙ je typu float, pak druh² by m∞l b²t p°eveden na typ float.
  35. Jinak by se m∞ly oba operandy podrobit celoΦφselnΘmu rozÜφ°enφ.
  36. Potom, jestli₧e jeden z operand∙ je typu unsigned long int, pak druh² by m∞l b²t p°eveden na typ unsigned long int.
  37. Jinak, jestli₧e jeden operand je typu long int a druh² typu unsigned int, pak jestli₧e long int m∙₧e reprezentovat vÜechny hodnoty typu unsigned int, operand typu unsigned int by m∞l b²t p°eveden na long int; jinak by oba operandy m∞ly b²t p°evedeny na unsigned long int.
  38. Jinak, jestli₧e jeden z operand∙ je typu long int, pak druh² by m∞l b²t p°eveden na typ long int.
  39. Jinak, jestli₧e jeden z operand∙ je typu unsigned int, pak druh² by m∞l b²t p°eveden na typ unsigned int.
  40. (Dßle u₧ zb²vß jen mo₧nost, ₧e oba operandy jsou typu int.)
  41. CeloΦφselnΘ rozÜφ°enφ je popsßno ve standardu v Φßsti 4.5. ZjednoduÜen∞ °eΦeno: MalΘ celoΦφselnΘ typy (char, signed char, unsigned char, short, unsigned short, wchar_t, bool) a v²ΦtovΘ typy mohou b²t p°evedeny na nejmenÜφ v∞tÜφ celoΦφseln² typ (nejmΘn∞ vÜak int nebo unsigned int), kter² pokr²vß cel² jejich rozsah.
  42. V p°φpad∞ unßrnφch aritmetick²ch operßtor∙ (tj. +, -) se pro celoΦφselnΘ a v²ΦtovΘ typy v₧dy provede celoΦφselnΘ rozÜφ°enφ. Tedy nap°φklad jestli₧e promenna je typu char, pak +promenna nebo -promenna je typu int.
  43. P°ekladaΦ vÜechna tato pravidla znß, ale pokud je chceme pou₧φt i pro ÜablonovΘ argumenty t°φd Φi funkcφ, musφme jim p°ekladaΦ znovu nauΦit - a to prßv∞ pomocφ metak≤du.
  44.  
  45. CeloΦφselnß rozÜφ°enφ
  46. ZaΦneme s celoΦφseln²m rozÜφ°enφm. Vytvo°φme metafunkci (t°φdu rys∙), kterß bude mφt za parametr typ. Pro v∞tÜinu typ∙ vrßtφ ten sam² typ, ale pro malΘ celoΦφselnΘ typy vrßtφ jejich celoΦφselnΘ rozÜφ°enφ.
  47. // primßrnφ Üablona
  48. template <class T> 
  49. struct CelociselneRozsireni
  50. {
  51.    typedef T RESULT;
  52. };
  53.  
  54. // explicitnφ specializace pro 
  55. // malΘ celoΦφselnΘ typy
  56. template <> 
  57. struct CelociselneRozsireni<char>
  58. {
  59.    typedef int RESULT;
  60. };
  61. // ...
  62. A tak podobn∞ pro ostatnφ malΘ celoΦφselnΘ typy - vÜe naleznete na Chip CD 5/01 ve zdrojovΘm souboru aritmeticke_konverze.h. Pokud se v programu objevφ
  63. CelociselneRozsireni<double>::RESULT
  64. nep∙jde o nic jinΘho ne₧ o typ double; zatφmco
  65. CelociselneRozsireni<char>::RESULT
  66. znamenß typ int, nebo¥ se provede rozÜφ°enφ.
  67.  
  68. Uspo°ßdßnφ typ∙
  69. Zde uva₧ujeme typy int, unsigned int a vÜechny dalÜφ vestav∞nΘ typy s v∞tÜφm rozsahem, nebo¥ spolΘhßme na p°edchozφ metafunkci pro celoΦφselnΘ rozÜφ°enφ. Vid∞li jsme, ₧e aritmetickΘ konverze jsou uspo°ßdßny podle rozsahu dan²ch typ∙. Proto definujeme pomocnou metafunkci (t°φdu rys∙), kterß uspo°ßdß typy podle rozsahu. Nejni₧Üφ po°adφ bude mφt typ int, nejvyÜÜφ pak long double. Primßrnφ Üablona bude tentokrßt prßzdnß - tφm sice znaΦn∞ omezφme poΦet pou₧iteln²ch typ∙ jen na t∞ch pßr vestav∞n²ch, ale pro naÜe ·Φely to zatφm staΦφ.
  70. // primßrnφ Üablona
  71. template <class T>
  72. struct PoradiTypu
  73. {
  74. };
  75.  
  76. // pro typ int
  77. template <> PoradiTypu<int>
  78. {
  79.    static const int Poradi = 1;
  80. };
  81.  
  82. // pro typ unsigned
  83. template <> PoradiTypu<unsigned>
  84. {
  85.    static const int Poradi = 2;
  86. };
  87.  
  88. // ...
  89. Pro ostatnφ typy se postupuje analogicky. Zbytek naleznete op∞t ve zdrojovΘm souboru aritmeticke_konverze.h. 
  90. To byl prvnφ krok. Te∩ musφme popsat, co se stane, kdy₧ se potkajφ dva typy v binßrnφm operßtoru. Pravidlo znßme: vybere se ten s v∞tÜφm rozsahem a druh² typ se na n∞j p°evede (pokud nejsou stejnΘ). Pot°ebujeme tedy implementovat rozhodovacφ schΘma.
  91.  
  92. Rozhodovacφ schΘma
  93. Pomocφ metafunkce chceme rozhodnout, kter² z typ∙ T1 a T2 mß vetÜφ rozsah. Rozhodovßnφ se °φdφ pravidlem, ₧e v∞tÜφ rozsah mß typ s vyÜÜφm po°adφm. Rozhodovacφ metafunkce nßm odpovφ na otßzku, zda mß typ T1 v∞tÜφ rozsah (je lepÜφ).
  94. template <class T1, class T2> 
  95. struct JeTypT1Lepsi
  96. {
  97.    static const bool RESULT = 
  98.       (PoradiTypu<T1>::Poradi > 
  99.       PoradiTypu<T2>::Poradi);
  100. };
  101. Toto je velmi jednoduchΘ rozhodovacφ schΘma, v n∞m₧ jsme se omezili pouze na vestav∞nΘ aritmetickΘ typy. Dalo by se ovÜem rozÜφ°it i o zpracovßnφ t°φd, nap°. std::complex<T>. Zßjemce odkazujeme na knihovnu Blitz++ (viz infotipy).
  102. U₧ mßme rozhodovacφ metafunkci, ale jeÜt∞ se neumφme postarat o v²b∞r sprßvnΘho typu. NapφÜeme proto dalÜφ pomocnou metafunkci:
  103. template <class T1, class T2, bool VYBER_T1 = true>
  104. struct VyberLepsiTyp
  105. {
  106.    typedef T1 RESULT;
  107. };
  108.  
  109. // ΦßsteΦnß specializace
  110. template <class T1, class T2>
  111. struct VyberLepsiTyp<T1, T2, false>
  112. {
  113.    typedef T2 RESULT;
  114. };
  115. Je jist∞ z°ejmΘ, ₧e t°etφm argumentem Üablony bude v²sledek rozhodovacφ metafunkce. Jde v podstat∞ o implementaci ·plnΘho meta-if. V p°φpad∞ spln∞nφ podmφnky (T1 je lepÜφ) se vybere prvnφ typ (T1), v opaΦnΘm p°φpad∞ druh² typ (T2).
  116.  
  117. A je to tady!
  118. Nynφ u₧ m∙₧eme p°istoupit k definici v²slednΘ metafunkce implementujφcφ obvyklΘ aritmetickΘ konverze:
  119. template <class T1, class T2>
  120. struct AritmetickaKonverze
  121. {
  122. private:
  123.    // nejprve celoΦφselnΘ rozÜφ°enφ
  124.    typedef typename 
  125.       CelociselneRozsireni<T1>::RESULT T1X;
  126.    typedef typename 
  127.       CelociselneRozsireni<T2>::RESULT T2X;
  128. public:
  129.    // koneΦn² v²sledek
  130.    typedef typename 
  131.       VyberLepsiTyp<T1X, T2X, 
  132.          JeTypT1Lepsi<T1X, T2X>::RESULT
  133.          >::RESULT RESULT;
  134. };
  135. A to u₧ je vÜe. Tak₧e jakmile se v programu objevφ
  136. AritmetickaKonverze<int, double>::RESULT
  137. AritmetickaKonverze<char, double>::RESULT
  138. p∙jde v obou p°φpadech o typ double. 
  139. Pokud si vzpomφnßte na funkci soucet z Φlßnku o t°φdßch rys∙, pak jejφ vylepÜenß verze by vypadala takto:
  140. template <class LEVY, class PRAVY>
  141. typename AritmetickaKonverze<LEVY, PRAVY>::RESULT
  142. soucet(LEVY levy, PRAVY pravy)
  143. {
  144.    typedef typename
  145.       AritmetickaKonverze<LEVY, PRAVY>::RESULT result_t;
  146.    return result_t(levy + pravy);
  147. }
  148.  
  149. P°φklad praktickΘho pou₧itφ
  150. Minule jsme vytvo°ili implementaci Üablon v²raz∙, ale bez syntΘzy typu. Nynφ to, jak jsme slφbili, vylepÜφme. P∙vodnφ implementaci Üablon v²raz∙ naleznete na Chip CD v souboru et.cpp a novß, vylepÜenß implementace je v souboru et2.cpp. Zde se zam∞°φme pouze na zm∞ny (v dalÜφm textu zv²razn∞ny). P°edn∞ je t°eba upravit t°φdy rys∙ pro elementßrnφ operace.
  151.  
  152. template <class TYP>
  153. struct UnarniMinus
  154. {
  155.    typedef typename
  156.       CelociselneRozsireni<TYP>::RESULT T;
  157.    static T apply(TYP x)
  158.    {
  159.       return -x;
  160.    }
  161. };
  162.  
  163. template <class T1, class T2>
  164. struct BinarniMinus
  165. {
  166.    typedef typename
  167.       AritmetickaKonverze<T1, T2>::RESULT T;
  168.    static T apply(T1 x, T2 y)
  169.    {
  170.       return x - y;
  171.    }
  172. };
  173. Dßle musφme upravit operßtory. Zde uvedeme pouze deklarace, definice naleznete ve zdrojovΘm souboru et2.cpp.
  174. template <class V1, class V2>
  175. Vyraz<BinaryOp<Vyraz<V1>, 
  176.     Vyraz<V2>, BinarniPlus<
  177.        typename Vyraz<V1>::T>,
  178.        typename Vyraz<V2>::T> >
  179. operator +(const Vyraz<V1>& v1,
  180.     const Vyraz<V2> & v2);
  181.  
  182. template <class T1, class T2>
  183. Vyraz<BinaryOp<
  184.     Promenna<Vektor<T1> >,
  185.     Promenna<Vektor<T2> >,
  186.     BinarniPlus<T1, T2> > >
  187. operator +(const Vektor<T1>& v1,
  188.     const Vektor<T2> & v2);
  189.  
  190. template <class T, class V>
  191. Vyraz<BinaryOp<
  192.     Promenna<Vektor<T> >,
  193.     Vyraz<V>, 
  194.     BinarniPlus<T,
  195.        typename Vyraz<V>::T> > >
  196. operator +(const Vektor<T>& v1,
  197.     const Vyraz<V> & v2);
  198. PomocnΘ adaptΘry (Vyraz, UnaryOp, BinaryOp) nenφ t°eba m∞nit. Jsou implementovßny natolik obecn∞, ₧e si bez problΘm∙ poradφ i se syntΘzou typu. 
  199. A nynφ jednoduch² p°φklad. M∞jme dva vektory
  200. Vektor<int> a;
  201. Vektor<double> b;
  202. JakΘho typu budou slo₧ky vektoru a + b? 
  203. P°etφ₧en² operßtor + vrßtφ t°φdu typu
  204. Vyraz<BinaryOp<Promenna<Vektor<int> >, 
  205.     Promenna<Vektor<double> >, 
  206.     BinarniPlus<int, double> > >
  207. Odtud m∙₧eme hledan² typ "vypoΦφtat" - zjednoduÜen∞ zapsßno:
  208. Vyraz<...>::T = BinaryOp<...>::T = 
  209.    = BinarniPlus<int, double>::T =
  210.    = AritmetickaKonverze<int, double>::RESULT =
  211.    = double
  212. Tak₧e v²sledek je typu Vektor<double>. Pro slo₧it∞jÜφ v²razy je situace obdobnß. Kdy₧ se na to podφvßme z pohledu ÜablonovΘho metaprogramovßnφ, m∙₧eme typ Vyraz<...> pova₧ovat za metastrom - analogii znßmΘ datovΘ struktury. SyntΘza typu je pak rekurzivnφ zpracovßnφ metadat (typ∙) v metastromu, to vÜe v dob∞ p°ekladu.
  213.  
  214. Zßv∞r?
  215. Tφmto Φlßnkem prozatφm konΦφ nßÜ miniserißl v∞novan² pokroΦil²m programovacφm technikßm pomocφ Üablon. KonΦφ ale definitivn∞? P°iblφ₧ili jsme si koncepty metaprogramovßnφ, t°φd rys∙, Üablon v²raz∙, ale na hodn∞ dalÜφch zajφmav²ch zßle₧itostφ se nedostalo. Nemluvili jsme nap°φklad o typov²ch metaseznamech, t°φdßch politik, multimetodßch a o °ad∞ dalÜφch pozoruhodn²ch programovacφch technik. Mo₧nß se k uveden²m tΘmat∙m jeÜt∞ n∞kdy vrßtφme.
  216.  
  217. Jaroslav Fran∞k
  218.  
  219. infotipy
  220. Blitz++: 
  221. http://oonumerics.org/blitz
  222. PETE: 
  223. http://www.acl.lang.gov/pete
  224. Standard C++: 
  225. International standard ISO/IEC 14882, 1998-09-01
  226. Souvisejφcφ Φlßnky:
  227. èablony po Üesti letech, Chip 12/00, str. 192 - 196
  228. D°inu nechte p°ekladaΦi!, Chip 1/01, str. 142 - 145
  229. Specializace trochu jinak, Chip 2/01, str. 142 - 144
  230. èablony v²raz∙ v C++, Chip 4/01, str. 172 - 175
  231. Ukßzky program∙: 
  232. Chip CD 5/01, rubrika Chip Plus
  233. -------
  234.  
  235. Errata
  236. V Φlßnku "èablony v²raz∙ v C++" v minulΘm Φφsle se bohu₧el vyskytlo n∞kolik nedopat°enφ, za kterΘ se vßm autor touto cestou omlouvß:
  237. - V definici operßtoru = (strana 173 uprost°ed) chybφ p°φkaz return *this; - ve zdrojovΘm souboru vÜak je vÜe v po°ßdku. 
  238. - P°i poΦeÜ¥ovßnφ jmen prom∞nn²ch (kv∙li v∞tÜφ p°ehlednosti) doÜlo k n∞kolika opomenutφm. Tak₧e UnaryOp a UnarniOp je stejnß t°φda, stejn∞ jako BinaryOp a BinarniOp. Zdrojov² k≤d ale pou₧φvß UnaryOp a BinaryOp. 
  239. - Nejv∞tÜφ nep°φjemnostφ je popis "Jak to funguje", kde se v zßv∞ru pomφchala pφsmenka a, b, c.
  240.  
  241.     6/1
  242.  
  243.