#define add_1(a) a
#define add_2(a, b) a + b
#define add_3(a, b, c) a + add_2(b, c)
#define add(...) PASTE(add_, GET_ARG_COUNT(__VA_ARGS__)) (__VA_ARGS__)
Netcan 2022-09-07 @Shanghai
96年出生,18年毕业于合肥工业大学本科
某大厂高级工程师
机工社 《C++20高级编程》作者
知乎 《魅力C++》 专栏作者
兴趣:CS, OO, FP, Design, Coding, Writing
Skills: C/C++/Rust, Haskell/Scheme, Bash/Python
各语言中的可变参数
C++中的可变参数
可变模板参数包
参数包展开、遍历
元编程灵魂:折叠表达式
遍历tuple
构建eDSL(动态 vs 静态)
生成switch-case结构
剖析std::thread实现
静态反射 Reflection TS
预处理宏
#define add_1(a) a
#define add_2(a, b) a + b
#define add_3(a, b, c) a + add_2(b, c)
#define add(...) PASTE(add_, GET_ARG_COUNT(__VA_ARGS__)) (__VA_ARGS__)
Rust声明宏
macro_rules! add {
($($x:expr), *) => {
0 $( + $x )*
}
}
add!(1, 2, 3, 4)
C语言
int add(int n, ...) {
va_list args;
va_start(args, n);
int ret = 0;
while(n-- > 0)
ret += va_arg(args, int);
va_end(args);
return ret;
}
Bash语言
function add {
sum=0
for v in "$@"; do
sum=$((sum+$v))
done
echo $sum
}
add 1 2 3
Python语言
def add(*args):
return sum(args)
JavaScript语言
function add(...args) {
return args.reduce((c, p) => c + p, 0)
}
特点
存在参数包的概念
存在展开的概念
支持遍历的功能
库专用特性
缺点
预处理宏难以移植
缺少类型信息
类型不安全
性能差
template<typename... T>
constexpr auto add(T... v) {
return (0 + ... + v);
}
C++11起支持可变模板参数
C++17起支持折叠表达式
template<typename... Args>
void f(Args... args) {
constexpr auto s1 = sizeof...(Args); // 模板参数包
constexpr auto s2 = sizeof...(args); // 函数参数包
static_assert(s1 == s2);
}
模板参数包:存放函数参数包对应的类型
函数参数包:存放传递给函数的实参包
template<typename... Args>
void f(Args... args) {
std::tuple<Args...> tp = // 展开模板参数包
std::make_tuple(args...); // 展开函数参数包
}
f(1, "2", 3.0)
展开后的结果 https://cppinsights.io/s/b78454ba
template<>
void f<int, const char *, double>(int __args0, const char * __args1, double __args2) {
std::tuple<int, const char *, double> tp =
std::make_tuple(__args0, __args1, __args2);
}
递归遍历:
void print() { }
template<typename A, typename... Args>
void print(const A& arg, const Args&... args) {
std::cout << arg << std::endl;
print(args...); // 递归与参数包展开
}
右折叠:(pack op … [op init])
左折叠:([init op] … op pack)
template<int... Is> // 右折叠
constexpr int rsub = (Is - ... - 0); // (Is - ...)
template<int... Is> // 左折叠
constexpr int lsub = (0 - ... - Is);
// (1 - (2 - (3 - (4 - (5 - 0)))))
static_assert(rsub<1,2,3,4,5> == 3);
// (((((0 - 1) - 2) - 3) - 4) - 5)
static_assert(lsub<1,2,3,4,5> == -15);
利用折叠表达式遍历:
template<typename... Args>
void print(const Args&... args) {
((std::cout << args << std::endl), ...); // 逗号表达式、折叠表达式
}
template<typename... Ts>
void f(Ts... args) {
auto v = {args ...}; // 参数包展开
auto v_ = (args, ...); // 折叠表达式
}
template<>
void f<int, int, int, int>(int __args0, int __args1, int __args2, int __args3) {
std::initializer_list<int> v = {__args0, __args1, __args2, __args3};
int v_ = __args0 , (__args1 , (__args2 , __args3));
}
C++11 特化、递归
template<size_t N>
struct dumpImpl {
template<typename Tup>
void operator()(const Tup& tp) const {
dumpImpl<N-1>{} (tp);
std::cout << std::get<N>(tp) << " ";
}
};
template<>
struct dumpImpl<0> {
template<typename Tup>
void operator()(const Tup& tp) const {
std::cout << std::get<0>(tp) << " ";
}
};
template<typename... Ts>
void dump(const std::tuple<Ts...>& tp) {
dumpImpl<sizeof...(Ts) - 1>{} (tp);
}
auto tp = std::make_tuple(1, 2.0, 3.0f);
dump(tp); // 1 2 3
C++14 integer_sequence
template<typename Tup, size_t... I>
void dumpImpl(const Tup& tp, std::index_sequence<I...>) {
auto l = { ((std::cout << std::get<I>(tp) << " "), 0)... };
}
template<typename... Ts>
void dump(const std::tuple<Ts...>& tp) {
std::make_index_sequence<sizeof...(Ts)> seqs;
dumpImpl(tp, seqs);
}
C++20 折叠表达式 + apply + auto函数参数
template<typename... Ts>
void dump(const std::tuple<Ts...>& tp) {
std::apply([](const auto&... args) {
((std::cout << args << " "),...);
}, tp);
}
https://godbolt.org/z/v7PbYh6oP ~600 行汇编
auto v = html(ul( li("Coffee")
, li("Tea")
, li("Milk")),
ol( li("hello"),
li("world")));
HtmlDumpper{std::cout}(v);
输出
<html>
<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>
<ul>
<li>hello</li>
<li>world</li>
</ul>
</html>
struct LiTag {
std::string_view content;
};
auto li(std::string_view vs) {
return LiTag { vs };
}
struct UlTag {
template<typename... Args>
UlTag(Args... arg) {
(liTag_.emplace_back(arg), ...);
}
std::vector<LiTag> liTag_;
};
template<typename... LI>
auto ul(LI... li) {
return UlTag { li... };
}
struct OlTag {
template<typename... Args>
OlTag(Args... arg) {
(liTag_.emplace_back(arg), ...);
}
std::vector<LiTag> liTag_;
};
template<typename... LI>
auto ol(LI... li) {
return UlTag { li... };
}
struct HtmlTag {
template<typename... Es>
HtmlTag(Es... es) {
(elems_.emplace_back(es), ...);
}
using Elem = std::variant<UlTag, OlTag>;
std::vector<Elem> elems_;
};
template<typename... E>
auto html(E... e) {
return HtmlTag { e... };
}
https://godbolt.org/z/fG5sPE1c3 ~70 行汇编
constexpr auto v = html(ul( li<"Coffee">
, li<"Tea">
, li<"Milk">),
ol( li<"hello">,
li<"world">));
htmlDumpper(v);
template<typename... LI>
struct UlTag { };
template<typename... UI>
struct OlTag { };
template<typename... Es>
struct HtmlTag { };
template<StringLiteral content>
struct LiTag { };
template<typename... E>
constexpr auto html(E...) {
return HtmlTag<E...> {};
}
template<typename... LI>
constexpr auto ul(LI...) {
return UlTag<LI...>{};
}
template<typename... LI>
constexpr auto ol(LI...) {
return OlTag<LI...>{};
}
template<StringLiteral content>
constexpr auto li = LiTag<content>{};
通常的std::get使用
auto tp = std::make_tuple(1, 2.0, 3.0f);
std::get<2>(tp); // 3.0f
希望提供动态的get
template<typename... Ts>
const void* get(const std::tuple<Ts...>& tp, size_t index);
get(tp, 2);
预期:
template<typename... Ts>
const void* get(const std::tuple<Ts...>& tp, size_t index) {
switch (index) {
case 0: return &std::get<0>(tp);
case 1: return &std::get<1>(tp);
case 2: return &std::get<2>(tp);
default: return nullptr;
}
}
使用折叠表达式:
template<typename... Ts>
const void* get(const std::tuple<Ts...>& tp, size_t index) {
auto getE = [&]<size_t I>(std::in_place_index_t<I>, size_t index) -> const void* {
return I == index ? &std::get<I>(tp) : nullptr;
};
const void* res = nullptr;
[&]<size_t... Is>(std::index_sequence<Is...>) {
(((res = getE(std::in_place_index_t<Is>{}, index)) != nullptr) || ...);
}(std::make_index_sequence<sizeof...(Ts)>{});
return res;
}
std::thread
原型
template<typename Function, typename... Args>
explicit thread(Function&& f, Args&&... args);
// example
std::thread th(f, a, b);
th.join();
pthread
原型
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg);
struct thread {
template<typename F, typename... Args>
explicit thread(F f, Args... args) {
using FnArgs = std::tuple<F, std::tuple<Args...>>;
std::unique_ptr<FnArgs> argsTup { new FnArgs(f, {args...}) };
int rc = pthread_create(&handle_, nullptr, [](void* fnArgs) -> void *{
std::unique_ptr<FnArgs> argsTup { static_cast<FnArgs*>(fnArgs) };
auto& [fn, args] = *argsTup;
std::apply(fn, args);
return nullptr;
}, argsTup.get());
if (rc == 0) argsTup.release();
else throw;
}
void join() {
pthread_join(handle_, nullptr);
}
private:
pthread_t handle_;
};
定义数据结构
struct Point {
double x;
double y;
};
struct Rect {
Point p1;
Point p2;
};
template<typename T>
void dump(const T& v, int depth = 0);
dump(Rect{{1.2, 3.4}, {4.5, 5.6}});
输出结果
(Point) p1:
(double) x: 1.2
(double) y: 3.4
(Point) p2:
(double) x: 4.5
(double) y: 5.6
template<typename... T> struct TypeList {};
template<typename T>
void dump(const T& v, int depth = 0) {
auto indent = [depth] {
for (int i = 0; i < depth; ++i) std::cout << " ";
};
using TMembers = refl::get_data_members_t<reflexpr(T)>;
[&]<typename... M>(TypeList<M...>) {
([&]<typename M_>(M_) {
using MTypeObj = refl::get_type_t<M_>;
using MType = refl::get_reflected_type_t<MTypeObj>;
constexpr auto type_name = refl::get_name_v<MTypeObj>;
constexpr auto field_name = refl::get_name_v<M_>;
constexpr auto mptr = refl::get_pointer_v<M_>;
indent();
std::cout << "(" << type_name << ") " << field_name << ": ";
if constexpr(std::is_class_v<MType>) {
std::cout << std::endl;
dump(v.*mptr, depth + 1);
} else {
std::cout << v.*mptr << std::endl;
}
}(M{}), ...);
}(refl::unpack_sequence_t<TypeList, TMembers>{});
}