SIGPL Summer Workshop 2017 C++ 메타프로그래밍과 constexpr 김경진 Astersoft Microsoft MVP(Visual C++)
Agenda 메타프로그래밍 템플릿메타프로그래밍 constexpr
메타프로그래밍 meta- < 초월한 > < ~ 의범주를넘어서는 >
메타프로그래밍 metaphysics 일반적물리학범주를넘어서는학문 형이상학 metaprogramming 일반적프로그래밍의범주를넘어서는프로그래밍 기존프로그래밍과는다른무언가를의미
메타프로그래밍 일반적프로그래밍 사용자의데이터를처리하고가공하는프로그램을만드는것 메타프로그래밍 프로그램을데이터로처리하고가공하는프로그램을만드는것 예 ) C++ 컴파일러, YACC 파서생성기
C++ 메타프로그래밍 일반적프로그래밍 런타임에수행할작업을프로그래밍하는것 메타프로그래밍 컴파일타임에수행할작업을프로그래밍하는것
Why C++ Metaprogramming? 런타임에수행할작업을컴파일타임에미리수행하여상수화 컴파일시간은다소늘어나지만런타임퍼포먼스는증가 계산결과가언어와더밀접하게상호작용가능 계산결과를배열의크기와같은상수에사용가능
템플릿메타프로그래밍의탄생 1994 년 Erwin Unruh 의우연한발견 템플릿을이용한미완의소수계산코드구현
템플릿메타프로그래밍의탄생 Error "primes.cpp",l16/c63(#416): prim Type `enum} can t be converted to txpe `D<2> ("primes.cpp",l2/c25). Error "primes.cpp",l11/c25(#416): prim Type `enum} can t be converted to txpe `D<3> ("primes.cpp",l2/c25). Error "primes.cpp",l11/c25(#416): prim Type `enum} can t be converted to txpe `D<5> ("primes.cpp",l2/c25). Error "primes.cpp",l11/c25(#416): prim Type `enum} can t be converted to txpe `D<7> ("primes.cpp",l2/c25). Error "primes.cpp",l11/c25(#416): prim Type `enum} can t be converted to txpe `D<11> ("primes.cpp",l2/c25). Error "primes.cpp",l11/c25(#416): prim Type `enum} can t be converted to txpe `D<13> ("primes.cpp",l2/c25). Error "primes.cpp",l11/c25(#416): prim Type `enum} can t be converted to txpe `D<17> ("primes.cpp",l2/c25). Error "primes.cpp",l11/c25(#416): prim Type `enum} can t be converted to txpe `D<19> ("primes.cpp",l2/c25). Error "primes.cpp",l11/c25(#416): prim Type `enum} can t be converted to txpe `D<23> ("primes.cpp",l2/c25). Error "primes.cpp",l11/c25(#416): prim Type `enum} can t be converted to txpe `D<29> ("primes.cpp",l2/c25). 런타임이아닌컴파일타임에무언가를계산할수있다 는가능성을보여줌
타입다루기 템플릿은특정타입에의존하지않고재사용성을 높이기위한목적으로만들어짐 템플릿인자 : 타입 template <typemame T>
타입다루기 Demo
is_same_type template <typename T1, typename T2> struct is_same_type enum value = false }; }; template <typename T> struct is_same_type<t, T> enum value = true }; }; int main() cout << is_same_type<int, double>::value << endl; cout << is_same_type<int, int>::value << endl; } 0 1
type_traits Primary Type Composite Type Type Properties Type Relationships is_null_pointer is_fundamental is_const is_same is_integral is_arithmetic is_volatile is_base_of is_floating_point is_scalar is_trivial is_convertible is_array is_object is_trivially_copyable is_invocable is_enum is_compound is_standard_layout is_nothrow_invocable is_union is_reference is_pod is_function is_member_pointer is_literal_type is_pointer is_empty is_lvalue_reference is_polymorphic
type_traits 활용예 template <typename T> void foo(const T& data) static_assert(std::is_integral<t>::value, "Template argument must be a integral type."); } int main() foo(1.5); // Compile Error } error C2338: Template argument must be a integral type.
데이터다루기 템플릿인자에는타입뿐만아니라데이터역시 사용이가능함 템플릿인자 : 데이터 template <int val>
데이터다루기 Demo
add / substract template <int left, int right> struct add enum value = left + right }; }; template <int left, int right> struct substract enum value = left - right }; }; int main() cout << add<10, 20>::value << endl; cout << substract<10, 20>::value << endl; } 30-10
factorial template <int val> struct factorial enum value = val * factorial<val - 1>::value }; }; template <> struct factorial<0> enum value = 1 }; }; int main() cout << factorial<10>::value << endl; } 3628800
fibonacci_number template <int val> struct finonacci_number enum value = finonacci_number<val - 1>::value + finonacci_number<val - 2>::value }; }; template <> struct finonacci_number<0> enum value = 0 }; }; template <> struct finonacci_number<1> enum value = 1 }; }; int main() cout << finonacci_number<10>::value << endl; } 55
is_prime struct false_type typedef false_type type; enum value = 0 }; }; struct true_type typedef true_type type; enum value = 1 }; }; template<bool condition, class T, class U> struct if_ typedef U type; }; template <class T, class U> struct if_ < true, T, U > typedef T type; }; template<size_t N, size_t c> struct is_prime_impl typedef typename if_<(c*c > N), true_type, typename if_ < (N % c == 0), false_type, is_prime_impl<n, c + 1> > ::type > ::type type; enum value = type::value }; }; template<size_t N> struct is_prime enum value = is_prime_impl<n, 2>::type::value }; }; template <> struct is_prime <0> enum value = 0 }; }; template <> struct is_prime <1> enum value = 0 }; };
템플릿메타프로그래밍의한계 컴파일타임계산만가능 가독성이매우낮음
vector<vector<int>> user-defined literals thread_local vector<localtype> initializer lists constexpr template aliases lambdas [] foo(); } unique_ptr<t> shared_ptr<t> weak_ptr<t> nullptr =default, =delete regex C++ raw string literals override, final auto i = v.begin(); thread, mutex for (x : coll) async atomic<t> variadic templates template <typename T > function<> strongly-typed enums enum class E }; auto f() -> int array<t, N> noexcept decltype extern template unordered_map<int, string> delegating constructors rvalue references (move semantics) future<t> static_assert(x) tuple<int, float, string>
constexpr specifier const, static 과용법이같은한정자 ( 변수, 함수에사용 ) 컴파일타임에값을도출하겠다 라는의미를부여 C++ 메타프로그래밍을문법차원에서지원
constexpr 변수 변수의값을컴파일타임에결정하여상수화하겠다 라는의미 반드시상수식으로초기화되어야함 constexpr int n = 0; constexpr int m = std::time(null); // OK // error C2127
constexpr 함수 함수파라미터에상수식이전달될경우, 함수내용을 컴파일타임에처리하겠다 라는의미 constexpr int square(int x) return x * x; } 전달되는파라미터에따라컴파일타임, 런타임처리가결정됨 int n; std::cin >> n; square(10); square(n); // 컴파일타임처리 // 런타임처리
constexpr 함수제한조건 함수내에서는하나의표현식만사용할수있으며, 반드시리터럴타입을반환해야함 * 리터럴타입 : 정수, 부동소수, 열거형, 포인터, 참조등의타입을지칭 constexpr LiteralType func() return expression; }
constexpr 함수로변환 if / else 구문 삼항연산자 (x > y? x : y) for / while 루프 재귀호출 변수선언 파라미터전달
constexpr 을이용한메타프로그래밍 Demo
factorial constexpr int factorial(int n) return n == 0? 1 : n * factorial(n - 1); } int main() int n; std::cin >> n; } constexpr int c_result = factorial(10); int r_result = factorial(n);
fibonacci_number constexpr int fibonacci_number(int n) return n <= 1? n : fibonacci_number(n - 1) + fibonacci_number(n - 2); } int main() int n; std::cin >> n; } constexpr int c_result = fibonacci_number(10); int r_result = fibonacci_number(n);
is_prime constexpr bool is_prime(int n, int i = 2) return n <= 1? false : i * i > n? true : n % i == 0? false : is_prime(n, i + 1); } int main() int n; std::cin >> n; } constexpr bool c_result = is_prime(13); bool r_result = is_prime(n);
컴파일타임문자열해시 constexpr unsigned int hash_code(const char* str) return str[0]? static_cast<unsigned int>(str[0]) + 0xEDB8832Full * hash_code(str + 1) : 8603; }
컴파일타임문자열해시 constexpr unsigned int hash_code(const char* str) return str[0]? static_cast<unsigned int>(str[0]) + 0xEDB8832Full * hash_code(str + 1) : 8603; } void foo(const char* name) switch (hash_code(name)) case hash_code("kim"): break; } } case hash_code("lee"): break;
C++14 constexpr 제한조건완화 변수선언가능 (static, thread_local 제외 ) if / switch 분기문사용가능 range-based for 루프를포함한모든반복문사용가능
is_prime C++14 ver. constexpr bool is_prime(int n) if (n <= 1) return false; } for (int i = 2; i * i <= n; ++i) if (n % i == 0) return false; } return true;
constexpr 관련라이브러리 Sprout C++ Libraries (Bolero MURAKAMI) http://bolero-murakami.github.io/sprout/ CEL constexpr Library (@sscrisk) https://github.com/sscrisk/cel---constexpr-library
참고자료 C++ Template Metaprogramming (David Abrahams, Aleksey Gurtovoy) http://blogs.embarcadero.com/jtembarcadero/2012/11/12/my-top-5-c11-language-and-library-features-countdown/ http://en.cppreference.com/w/cpp/language/constexpr http://en.cppreference.com/w/cpp/language/constexpr http://blog.smartbear.com/c-plus-plus/using-constexpr-to-improve-security-performance-and-encapsulation-in-c/ http://www.codeproject.com/articles/417719/constants-and-constant-expressions-in-cplusplus http://cpptruths.blogspot.kr/2011/07/want-speed-use-constexpr-meta.html http://enki-tech.blogspot.kr/2012/09/c11-compile-time-calculator-with.html http://en.wikipedia.org/wiki/c%2b%2b14 https://www.slideshare.net/embo_conference/programming-at-compile-time