Metaohjelmointia ja muuta hauskaa Juho Lauri C++11-seminaari Kevät 2012
Sisältö 1 Metaohjelmat ja -ohjelmointi 2 Funktiowrapperit ja sitominen 3 std::result of
Metaohjelmat ja -ohjelmointi Ensimmäisiä dokumentoituja kosketuksia metaohjelmointiin Erwin Unruhin metaohjelma standardointikomitean kokouksessa 1994 ohjelma antoi virheilmoituksena alkulukuja Myöhemmin suhtautuminen metaohjelmointiin muuttui vakavammaksi Veldhuizen T., Using C++ template metaprograms, C++ Report Vol. 7 No. 4 (May 1995), pp. 36-43. Nykyään paremmin ymmärretty ja tuettu konsepti Boost: Function.Types, Fusion, MPL, Proto, Static.Assert, Type.Traits,...
Metaohjelmat ja -ohjelmointi Vanhassa C++:ssa tällaisia type traits -luokkia on jo nähty minkä tyyppisiä otuksia Iteraattori-tyyppini käsittelee? std::iterator traits<iteraattori>::value type voiko lukutyyppini X esittää arvon ääretön? std::numeric limits<x>::has infinity Uusi < type traits > tukee metaohjelmointia ja geneeristä ohjelmointia; mahdollista esim. selvittää tyypille päteviä ominaisuuksia (esim. std::is floating point) muokata tyyppejä (esim. std::add const) vertailla tyyppejä (esim. std::is same)
Metaohjelmat ja -ohjelmointi Uutena ominaisuutena myös käännöksenaikainen static assert(lauseke, virheviesti): template <typename T, typename U> void foo(t x, U y) { // Vaaditaan, että T ja U ovat samankokoisia static_assert(sizeof(t) == sizeof(u), "T ja U erikokoisia"); // Vaaditaan, että T ja U ovat samaa tyyppiä static_assert(std::is_same<t,u>::value, "T!= U"); } // Voisiko tyyppiä T kopioida turvallisesti // std::memset-funktiolla? bool opt = std::is_trivially_copyable<t>::value;
Metaohjelmat ja -ohjelmointi yhteenveto C++11 tukee enemmän metaohjelmointia ja geneeristä ohjelmointia uudet avainsanat constexpr ja static assert < type traits >: reilu 100 erilaista traitsia Suurin hyöty (jälleen) lähinnä kirjastojen ym. kehittäjille
Funktiowrapperit ja sitominen Suurehkoja muutoksia ja lisäyksiä < functional > otsikkoon std::function geneerinen, tyyppiturvallinen wrapperi kutsuttaville otuksille std::bind tehokkaampi ja käyttäjäystävällisempi sidonta Vanhat std::bind1st ja std::bind2nd vanhentuneiksi (deprecated)
Funktiowrapperit ja sitominen // Suurin yhteinen tekijä luvuille x ja y int syt(int x, int y) {... } // Palauta int, parametrit: int, int typedef std::function<int (int, int)> fun; fun f = syt; std::cout << f(10, 5) << "\n"; // OK, vapaa funktio fun g = std::plus<int>(); std::cout << g(10, 5) << "\n"; // OK, funktori fun h; h(10, 5); assert(h); //!!, std::bad_function_call // OK, operator bool()
Funktiowrapperit ja sitominen std::bind Uusi std::bind tuottaa funktoreita kutsuttavista otuksista korvaus ja yleistys vanhoille (std::bind1st, std::bind2nd) tekee turhaksi erilaiset adapterit (std::ptr fun, std::mem fun, std::mem fun ref, std::mem fn) Olennainen osa ns. placeholderit (std::placeholders:: n) mahdollistavat argumentit luoduille funktoreille argumentit kopioidaan; syntyvä funktori säilyttää ne sisäisesti
Funktiowrapperit ja sitominen std::bind int kerro(int x, int y) { return x * y; } using std::placeholders::_1; std::function<int, (int)> f = std::bind(kerro, 10, _1); std::cout << f(5); // 10 * 5 == 50 auto g = std::bind(kerro, 10, 5); std::cout << g();
Funktiowrapperit ja sitominen std::bind struct foo { foo() : x_(10) { } void kasva(int y) { x_ *= y; } int x_; }; std::vector<foo> v(10); std::for_each(v.begin(), v.end(), std::bind(&foo::kasva, _1, 10)); // Sama ennen... std::for_each(v.begin(), v.end(), std::bind2nd(std::mem_fun_ref(&foo::kasva), 10));
Funktiowrapperit ja sitominen yhteenveto std::function geneerinen, tyyppiturvallinen wrapperi kutsuttaville otuksille eksplisiittinen vertailu (==,!=) kielletty std::bind tehokkaampi, käyttäjäystävällisempi sidontamekanismi poistaa tarpeen hankalilta adaptereilta
std::result of Metafunktio, joka päättelee funktorikutsun paluutyypin käännösaikana struct foo { double operator()(char, int&); }; int main() { std::result_of<foo(char, int&)>::type f = 3.14; // f on tyyppiä double }