Static If I Had a Hammer
Total Page:16
File Type:pdf, Size:1020Kb
Static If I Had a Hammer Andrei Alexandrescu Research Scientist Facebook c 2012– Andrei Alexandrescu, Facebook. 1/27 In a nutshell (1/2) enum class WithParent { no, yes }; template <class T, WithParent wp> class Tree { class Node { ... }; static if (wp == WithParent::yes) { Node * parent_; } Node * left_, * right_; T payload_; ::: }; Tree<string, WithParent::yes> dictionary; c 2012– Andrei Alexandrescu, Facebook. 2/27 In a nutshell (2/2) template <class It> It rotate(It b, It m, It e) if (is_forward_iterator<It>::value) { ::: } c 2012– Andrei Alexandrescu, Facebook. 3/27 Agenda • Motivation • The static if declaration • Template constraints • Implementation issues c 2012– Andrei Alexandrescu, Facebook. 4/27 • In a nutshell (1/2) • In a nutshell (2/2) • Agenda Motivation • Why static if? • Why not static if? • A non-starter: #if The static if declaration Template Constraints Motivation Implementation Issues Time for ritual speakercide c 2012– Andrei Alexandrescu, Facebook. 5/27 Why static if? • Introspection-driven code ◦ Acknowledged, encouraged: <traits> ◦ Incomplete: no trait-driven conditional compilation • Recursive templates impose signature duplication • Specialization on arbitrary type properties ◦ enable_if incomplete, syntax-heavy, has corner cases • Difficulty in defining policy-based designs • Opportunistic optimizations depending on type properties c 2012– Andrei Alexandrescu, Facebook. 6/27 Why not static if? • Would you define a new function for every if? • Makes advanced libraries ◦ Easy to implement and debug ◦ Easy to understand and use • Simple problems should have simple solutions ◦ “This function only works with numbers” ◦ “This algorithm requires forward iterators” ◦ “I can choose whether this tree has parent pointers” c 2012– Andrei Alexandrescu, Facebook. 7/27 A non-starter: #if • “Conditional compilation” associated with preprocessor • Preprocessing step long before semantic analysis • Poor integration with the language • Impossible to test any trait ◦ Or really anything interesting c 2012– Andrei Alexandrescu, Facebook. 8/27 • In a nutshell (1/2) • In a nutshell (2/2) • Agenda Motivation The static if declaration • Basics • Details • Use inside templates • std::pair done The static if right • std::tuple done simply declaration • Use inside functions Template Constraints Implementation Issues Time for ritual speakercide c 2012– Andrei Alexandrescu, Facebook. 9/27 Basics • Declaration-statement • May occur wherever a declaration or statement allowed static if (sizeof(size_t) == 8) { size_t bsr64(size_t bits, size_t amount) { ... } } // or size_t bsr64(size_t bits, size_t amount) { static if (sizeof(size_t) == 8) { ... } else { ... } } c 2012– Andrei Alexandrescu, Facebook. 10 / 27 Details • Tested expression is a constant-expression of if-testable type • The braces do not introduce a scope ◦ For grouping only; add one more layer to introduce a scope • No static else needed; else suffices • Code on the failing branch tokenized but not compiled ◦ Situation similar but not identical to uninstantiated templates c 2012– Andrei Alexandrescu, Facebook. 11 / 27 Use inside templates template <class T> struct container { . static if (debug_mode<T>::value) { class const_iterator { . }; static if (std::is_const<T>::value) { typedef const_iterator iterator; } else { class iterator { . }; } } else { class const_iterator { . }; class iterator { . }; } }; c 2012– Andrei Alexandrescu, Facebook. 12 / 27 std::pair done right template <class T, class U> struct pair { static if (is_empty<T>::value || is_empty<U>::value) { union { T first; U second; }; } else { T first; U second; } . }; c 2012– Andrei Alexandrescu, Facebook. 13 / 27 std::tuple done simply template <class... Ts> struct tuple { static if (sizeof...(Ts) > 0) { . } else { . } . }; c 2012– Andrei Alexandrescu, Facebook. 14 / 27 Use inside functions template <class It, class T> void uninitialized_fill(It b, It e, const T& x) { static if (is_same< typename iterator_traits<It>::iterator_category, random_access_iterator_tag>::value) { assert(b <= e); } static if (has_trivial_copy_constructor<T>::value) { // Doesn’t matter that values were uninitialized std::fill(b, e, x); } else { for (; b != e; ++b) { new(&*b) T(x); } } } c 2012– Andrei Alexandrescu, Facebook. 15 / 27 • In a nutshell (1/2) • In a nutshell (2/2) • Agenda Motivation The static if declaration Template Constraints • Insufficient • Template Constraints • “But we can do that already!” Template Constraints • Classes, too • Aftermath • The concept connection Implementation Issues Time for ritual speakercide c 2012– Andrei Alexandrescu, Facebook. 16 / 27 Insufficient • uninitialized_fill still unsatisfactory • Templates are prone to biting off more than they can chew • Any instantiation errors of uninitialized_fill prompt syntax errors inside its body • static_assert inside may help, but not with overloading c 2012– Andrei Alexandrescu, Facebook. 17 / 27 Template Constraints • Any template may have a constraint template <class It, class T> void uninitialized_fill(It b, It e, const T& x) if (is_forward_iterator<It>::value && is_convertible<T, decltype(*b)>::value) { . } c 2012– Andrei Alexandrescu, Facebook. 18 / 27 “But we can do that already!” template <class It, class T> typename enable_if< is_forward_iterator<It>::value && is_convertible< T, iterator_traits<It>::value_type >::value >::type uninitialized_fill(It b, It e, const T& x) { . } • Too important and ubiquitous to leave it to a noisy hack c 2012– Andrei Alexandrescu, Facebook. 19 / 27 Classes, too • And methods, of course template <class T> class rational if (is_integral<T>::value) { . rational operator-() if (is_signed<T>::value) { . } }; c 2012– Andrei Alexandrescu, Facebook. 20 / 27 Aftermath • Constraint “sees” symbols in the declaration ◦ Essential (terse, too) • Definitionless declarations may also be constrained • Constraint not part of the signature ◦ If constraint false, declaration disappears ◦ À la SFINAE c 2012– Andrei Alexandrescu, Facebook. 21 / 27 The concept connection • Lightweight concept system • Concepts expressed as Boolean assertions + Beautifully complements and integrates with traits + Makes concept definition as easy as writing Boolean axioms + Can express constraints as relations between types, not only individual types + Works with numbers too, not only types − Not designed for separate compilation of templates • Huge positive impact on the D programming language c 2012– Andrei Alexandrescu, Facebook. 22 / 27 • In a nutshell (1/2) • In a nutshell (2/2) • Agenda Motivation The static if declaration Template Constraints Implementation Issues • Back to conditional compilation Implementation Issues • Experience • Credits Time for ritual speakercide c 2012– Andrei Alexandrescu, Facebook. 23 / 27 Back to conditional compilation • How about the “dead” branches? • Possibility: treat like uninstantiated templates + Understood approach, easy to implement − Non-portable syntax not possible • Possibility: store as brace-balanced tokens − More difficult to implement + More flexible, faster compilation speeds +/− static if(false): glorified comment • Either way: cannot handle unpaired braces ◦ For the better, actually c 2012– Andrei Alexandrescu, Facebook. 24 / 27 Experience • Already defined, implemented, and used in D • Positive experience • Backwards compatible • Easy to incrementally retrofit code to using it • Popular with library writers and users alike c 2012– Andrei Alexandrescu, Facebook. 25 / 27 Credits • Walter Bright, Herb Sutter, yours truly: N3329 • Walter Brown: N3322 ◦ Smaller proposal ◦ Similarly motivated and defined ◦ No template constraints • A lot of alignment • Merging is a sensible course of action c 2012– Andrei Alexandrescu, Facebook. 26 / 27 • In a nutshell (1/2) • In a nutshell (2/2) • Agenda Motivation The static if declaration Template Constraints Implementation Issues Time for ritual Time for ritual speakercide speakercide c 2012– Andrei Alexandrescu, Facebook. 27 / 27.