Kartais universitetas priverčia išbandyti dalykus, kurie atrodo keistai, apie kuriuos anksčiau nežinojai, bet pamiršta pasakyti svarbiausią dalyką - koks jų praktinis pritaikymas.
Niekada negalvojau, jog įmanoma padaryti amžiną kompiliavimo ciklą. Dabar žinau - aš talentingas.
Norėčiau papasakoti, savo įspūdžius, kaip man pavyko susipažinti su c++ šablonais ir metaprogramavimu.
Metaprogramavimas - tai tokios programos rašymas, kuri generuoja kitą programą arba ja manipuliuoja. Programa, kuri kuriama arba kuria manipuliuojama, vadinama objektu ir pateikiama kaip duomenys. Programa, kuri atlieka manipuliacijas, vadinama metaprograma. Yra du meta programavimo būdai: homogeninis ir heterogeninis. Homogeninio programavimo metu objekto ir metaprogramos rašymui naudoja ta pati kalba, heterogeninio skirtingos.
Šį kartą aptarsiu tik homogeninį programavimo būdą. Pateiksiu pavyzdį c++ kalba, kurį man teko pasirašyti laboratorinio atlikimui ir paaiškinsiu kaip jis veikia. Bet prieš tai dar truputėlis teorijos:
Metaprogramoje nenaudojamos if-else sąlygos
if (salyga) teiginys1; else teiginys;
jos yra keičiamos į:
template<bool C> class _pavadinimas { }; class _pavadinimas<true> { public: static inline void funk() { teiginys1; } // true salyga }; class _pavadinimas<false> { public: static inline void funk() { teiginys2; } // false sąlyga }; // if-else sąlygos iškvietimas: _pavadinimas<salyga>::funk();
switch-case taip pat yra keičiamas:
int i; switch(i) { case reiksme1: teiginys1; break; case reiksme2: teiginys2; break; default: teiginys3; break; }
į tokį kodą:
//Klasės aprašas template<int I> class _pavadinimas { public: static inline void funk() { teiginys3; } }; class _pavadinimas<reiksme1> { public: static inline void funk() { teiginys1; } }; class _pavadinimas<reiksme2> { public: static inline void funk() { teiginys2; } }; // switch(i) sąlygos iškvietimas _pavadinimas<I>::funk();
while ciklas
int i = N; do { teiginys; } while (--i > 0);
keičiamas į:
// Klasės deklaracija template<int I> class _pavadinimas { private: enum { go = (I-1) != 0 }; public: static inline void funk() { statement; _pavadinimas<go ? (I-1) : 0>::funk(); // rekursijos kvietimas } }; class _pavadinimas<0> { // ką daryti kai pasiekiama minimali reikšmė (nulis)? public: static inline void f() { return 1; } }; // Ciklo kvietimas _pavadinimas<N>::funk();
Beje, atkreipkite dėmesį, jog tai atvirkščias do-while ciklas, sukamas nuo didžiausios reikšmės iki mažiausios.
Tiek turėtų užtekti, kad galėtume spręsti pagrindinius uždavinius, nors prie pilnos kompanijos čia dar trūksta for ciklo.
Laboratorinio užduotis:
Apibendrinkite etaloninę funkciją sinh(x) pagal 2 nepriklausomus parametrus: duomenų tipą {float, double} ir eilutės narių skaičių {2..8}. Naudoti C++ templates.
float _sinh_2 (float x) { return x * (1 + x*x/6); } double _sinh_3 (double x) { return x * (1 + x*x/6) * (1 + x*x/20)); } float _sinh_4 (float x) { return x * (1 + x*x/6 * (1 + x*x/20 * (1 + x*x/42))); }
Pagal duomenų tipą aš šį kartą neapibendrinsiu, tik pagal eilutės narių skaičių. Tiesą pasakius, užduotis gana kvaila, nes labai stipriai nukenčia tikslumas.
Pirmiausiai pasirašysime paprastą apibendrintą c++ funkciją, o po to ją versime į šabloną:
double sinh2(double x, int j) { double val = 1; int k=1; do {k++; val = 1 + x *x/((k-2+k)*(k-1+k))*val; }while(k!=j); return x * val; }
Jeigu atidžiai pažvelgsite į kodą iš karto pastebėsite, kaip pagaminti amžiną ciklą. Tiems, kurie neatkreipė dėmesio pašnibždėsiu, kai j<2. Suprasite, kodėl nedėjau if-else sąlygos, kai pažvelgsite truputį aukščiau, kur aš aiškinau kaip ją paversti į šabloną.
Prieš tęsiant, reiktų pasakyti dar keletą dalykų. Mokindamasis kurti c++ šablonus pastebėjau, jog kaip parametrus visuomet paduoda tik sveikus skaičius, o pagal užduotį man reikia paduoti float. Norėdamas išspręsti savo užduotį nagrinėjau kitą kodą, kuriame realizuota sin(), tenai reikšmę x keisdavo pagal formule x=I*2*M_PI/N, tiesa, aš nesu tikras ar mano reikalinga pi, vis tik sinh() yra hiperbolis sinusas.
#include "stdafx.h" #include <iostream> #define _USE_MATH_DEFINES // vėliau naudojam PI konstantą #include <math.h> using namespace std; //--------------------------------------------------------------------------- template<int N, int I, int J, int K> class SineSeries { public: enum { go = (K != J) }; static inline float accumulate() { return 1+(I*2*M_PI/N)*(I*2*M_PI/N)/((K-2+K)*(K-1+K)) * SineSeries<N*go,I*go,J*go,(K+1)*go>::accumulate(); } };
Pagrindinė funkcija, atlieka skaičiavimą ir iteraciją į save.
template<> class SineSeries<0,0,0,0> { public: static inline float accumulate() { return 1; } };
kai while ciklas pasiekia nulinę reikšmę iškviečiama šita funkcija.
template<int N, int I, int max> class Sine { public: static inline float sinh() { return (I*2*M_PI/N) * SineSeries<N,I,max,2>::accumulate(); } };
grąžinimas galutinis rezultatas.
void main() { Sine <10, 20, 2> obj; cout << "rezultatai:" << obj.sinh(); }
Pagrindinė funkcija, kuri inicijuoja naudojimąsi mano sukurtu šablonu. Trečias skaičius negali būti mažesnis už du, nes turėsite amžiną ciklą ant kompiliavimo.
Tai tokia mano pažintis su šablonais ir metaprogramavimu. Pagrindinis privalumas - skaičiavimai atliekami žymiai greičiau. Tačiau darydamas šį laboratorinį susiduriau net su keliais trūkumas: 1) iteracijos kodas man niekada nebuvo aiškus; 2) daug kodo, su paprastu ciklu tik 10 eilučių.
Tikiuosi kam nors padės šis straipsnelis, nes informacijos nėra daug šita tema, o kartais reikia.
Dėkoju visiems, kurie padėjo pasidaryti man laboratorinį darbą.
Dar informacijos galima rasti čia:
Atviras Kodas Wiki
2007-04-12 | 11:10
Žudantis klausimas būtent šito pavyzdžio realizacijai: o kaip su metaprogramavimu realizuotu sinh() apskaičiuoti reikšmę, kai argumentas yra įvedamas vartotojo?
Nes tavo pavyzdyje skaičiavimai nėra “atliekami greičiau”, jie iš viso nėra atliekami. Sine tiesiog pakeičiamas float konstanta ir tiek; pats galėtum ją su kalkuliatorium apskaičiuot ir įrašyt - ir kodo būtų žymiai mažiau ;)
Užduotis, kaip suprantu, buvo realizuoti duomenų tipo ir iteracijų kiekio šablonizavimą. Maždaug taip:
template
T SinH( T argument ) { blah blah; };
Jei su metaprogramavimu, tai turbūt tai atrodytų maždaug:
template
struct SinH {
T Evaluate( T argument ) { blah blah; }
};
t.y. argumentas yra argumentas, kokia prasmė jį kišti į šablono parametrus?
2007-04-12 | 11:11
Oh my, wordpress visiškai suvalgo templeitų kodą tarp ‘mažiau’ ir ‘daugiau’ simbolių :(
2007-04-12 | 11:57
liux straipsnis, pačiu laiku, kaip tik reiks parašyti programa su šablonais ;)
2007-04-12 | 12:11
Aš esu pasibaisėjęs tuo, ko moko universitetuose! :D
2007-04-12 | 13:00
Aras, kodą gali į paste.lt nupastinti ir čia tiesiog url įdėt ;) arba naudok <code>kodas</code>
2007-04-12 | 17:06
Už pirmą sakinį - 10+
2007-04-22 | 19:39
ar galima su sia programa lauzti pvz: kokionors acounto koda ar pan.? Pvz as turiu Log-in ir man reikia pass.. :D
2007-04-23 | 22:42
Su ja galima daug ka.Apie keyloggeri net nekalbu ;D.
2007-05-10 | 0:17
yra klaida siame straipsnyje neatinka iso c++ standarto ir delto nesikopiliuoje ant vc2005 reiketu sita pakeisti
template
class _pavadinimas {
public:
static inline void funk()
{ teiginys3; }
};
class _pavadinimas {
public:
static inline void funk()
{ teiginys1; }
};
class _pavadinimas {
public:
static inline void funk()
{ teiginys2; }
};
pakeisti i
template
class _pavadinimas {
public:
static inline void funk()
{ teiginys3; }
};
template
class _pavadinimas {
public:
static inline void funk()
{ teiginys1; }
};
template
class _pavadinimas {
public:
static inline void funk()
{ teiginys2; }
}
eiline vc2005 nesamone.
2008-07-12 | 20:13
Heh, seniau aktyviai su tuo zaisdavau, aplamai C++ galimybes ir perspektyva domino, cia viena naudinga mpl parasiau - saknies traukimas ir tikrinimas, gal kam pravers :)
template struct is_sqrt {
enum { ATS = is_sqrt::ATS };
};
template struct is_sqrt {
enum { ATS = true };
};
template struct is_sqrt {
enum { ATS = false };
};
template struct is_sqrt {
enum { ATS = false };
};
int main() {
// ptr::basetype obj(5);
cout << is_sqrt::ATS;
cin.get();
return 0;
};