模板元编程
广义的元编程是指为生成代码进行的编程,例如词法/语法编译器flex/bison可根据自定义语言生成词法及语法解释器C语言代码,这可算入广义元编程的范畴
在C++中,元编程专指针对类型以及常数进行推导、演算和构造等操作,这些操作的共同特点都是面向编译期逻辑,大多通过模板技巧实现,是一种狭义的元编程。
helloworld
/**
* @brief 元编程helloworld
* @filename meta_demo.cpp
*/
#include <iostream>
#include <cstdint>
template <uint64_t N>
struct Factorial {
//enum { Value = N * Factorial<N-1>::Value };
static const uint64_t Value = N * Factorial<N-1>::Value;
};
template<>
struct Factorial<1> {
//enum { Value = 1 };
static const uint64_t Value = 1;
};
int TestFactorial()
{
auto fact7 = Factorial<7>::Value;
auto fact23 = Factorial<23>::Value;
return 0;
}
int main(int argc, char *argv[]) {
TestFactorial();
return 0;
}
复习编译过程
预编译
# -E,只执行到预编译。直接输出预编译结果
gcc -E hello.c -o hello.i
编译
# -S,只执行到源代码到汇编代码的转换,输出汇编代码。
gcc -S hello.i -o hello.s
# 或者
gcc -S hello.c -o hello.s
汇编
# -c,只执行到编译,输出目标文件。
gcc -c hello.s -o hello.o
# 或者
gcc -c hello.c -o hello.o
链接
TODO: 待补充
验证编译期求值
进行验证fact7
和 fact23
的数值是编译器进行计算出来的:
g++ -S meta_demo.cpp -o meta_demo.s
.file "meta_demo.cpp"
.text
.section .rodata
.type _ZStL19piecewise_construct, @object
.size _ZStL19piecewise_construct, 1
_ZStL19piecewise_construct:
.zero 1
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.text
.globl _Z13TestFactorialv
.type _Z13TestFactorialv, @function
_Z13TestFactorialv:
.LFB1522:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq $5040, -16(%rbp)
movabsq $8128291617894825984, %rax
movq %rax, -8(%rbp)
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1522:
.size _Z13TestFactorialv, .-_Z13TestFactorialv
.globl main
.type main, @function
main:
.LFB1523:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
call _Z13TestFactorialv
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1523:
.size main, .-main
.type _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2004:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
cmpl $1, -4(%rbp)
jne .L7
cmpl $65535, -8(%rbp)
jne .L7
leaq _ZStL8__ioinit(%rip), %rdi
call _ZNSt8ios_base4InitC1Ev@PLT
leaq __dso_handle(%rip), %rdx
leaq _ZStL8__ioinit(%rip), %rsi
movq _ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %rax
movq %rax, %rdi
call __cxa_atexit@PLT
.L7:
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2004:
.size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
.type _GLOBAL__sub_I__Z13TestFactorialv, @function
_GLOBAL__sub_I__Z13TestFactorialv:
.LFB2005:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $65535, %esi
movl $1, %edi
call _Z41__static_initialization_and_destruction_0ii
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2005:
.size _GLOBAL__sub_I__Z13TestFactorialv, .-_GLOBAL__sub_I__Z13TestFactorialv
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I__Z13TestFactorialv
.hidden __dso_handle
.ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
求解斐波那契数列
template <uint64_t N>
struct Fibonacci {
static const uint64_t Value = Factorial<N-1>::Value + Factorial<N-2>::Value;
};
template<>
struct Fibonacci <1> {
static const uint64_t Value = 1;
};
template<>
struct Fibonacci <0> {
static const uint64_t Value = 0;
};
int TestFibonacci()
{
auto fib10 = Fibonacci<10>::Value;
auto fib45 = Fibonacci<45>::Value;
return 0;
}