周刊项目地址[1]
RSS https://github.com/wanghenshui/cppweeklynews/releases.atom
欢迎投稿,推荐或自荐文章/软件/资源等
请提交 issue[2]
感谢不语
赞助
资讯
标准委员会动态/ide/编译器信息放在这里
编译器信息最新动态推荐关注hellogcc公众号 本周更新 2023-08-09 第214期
boost::async review https://github.com/klemens-morgenstern/async
https://www.reddit.com/r/cpp/comments/15l8xju/review_of_proposed_boostasync_begins/
比较集成在ASIO上
文章
cppwinrt is in maintenance mode[3]
感觉cppwinrt还没开发多久,就维护状态了,又引入了新库 https://github.com/microsoft/wil/
说实话,微软总搞这种事,开发一个新技术,让大伙学,还没怎么学明白,换坑,再重新学,和google有一拼
之前更新了很多winrt的文章,得亏一个没看,他妈的,纯纯浪费生命,微软我___
Did you know that C 26 added 'A nice placeholder with no name' [4]
代码语言:javascript复制auto foo() { return 42; }
int main() {
auto unused = foo(); // warning in C 23
auto _ = foo(); // no warning in C 26
}
话说,我以前是这么玩的, std::ignore[5]
代码语言:javascript复制decltype(std::ignore) _;
_ = blahblah();
Inside STL: The lists[6]
介绍链表实现,MSVC和gcc/clang的实现还不太一样, 单向链表都差不多
代码语言:javascript复制template<typename T>
struct forward_list {
forward_list_node<T>* head;
};
template<typename T>
struct forward_list_node {
forward_list_node<T>* next;
T value;
};
双向链表,有个边界判定问题
代码语言:javascript复制template<typename T>
struct list {
list_node_base<T> head; // or "list_node_base<T>* head;"
size_t size;
};
template<typename T>
struct list_node_base {
list_node<T>* next;
list_node<T>* prev;
};
template<typename T>
struct list_node : list_node_base<T> {
T value;
};
可以有个dummy node来管理开头结尾,也可不用,多一个内存分配,这也是msvc需要注意的地方,list构造可能抛异常
Inside STL: The map, set, multimap, and multiset[7]
基本结构就这样
代码语言:javascript复制struct tree {
node_base header; // or "node_base* header;"
size_t size;
};
struct node_base {
node_base* parent;
node_base* left;
node_base* right;
};
struct node : node_base {
bool color; // red or black
payload data;
};
Five Advanced Initialization Techniques in C : From reserve() to piecewise_construct and More.[8]
介绍几种高效的用法,比如vector
reserve
emplace_back
,这种大家都知道,代码就不贴了
lambda 捕获优化
代码语言:javascript复制auto result = std::find_if(vs.begin(), vs.end(),
[&prefix](const std::string& s) {
return s == prefix "bar"s;
}
);
这种使用捕获用法,每次都要生成对象,效率低下,需要改成
代码语言:javascript复制result = std::find_if(vs.begin(), vs.end(),
[savedString = prefix "bar"s](const std::string& s) {
return s == savedString;
}
);
这样,避免每次lambda都构造
unique_ptr
优化 make_unique_for_overwrite
,有时候你想用unique_ptr
管理 buffer,但没有必要清零,可以用这个接口,类似string
的 resize_for_overwrite
auto ptr = std::make_unique_for_overwrite<int[]>(1000);
pair/tuple优化 piecewise_construct
forward_as_tuple
// 1
std::cout << "regular: n";
std::pair<MyType, MyType> p { MyType{"one", 1}, MyType{"two", 2}};
// 2
std::cout << "piecewise forward: n";
std::pair<MyType, MyType>p2(std::piecewise_construct,
std::forward_as_tuple("one", 1),
std::forward_as_tuple("two", 2));
起到一个就地构造的效果,类似emplace_back
对于map来说,这种也能用
代码语言:javascript复制#include <string>
#include <map>
struct Key {
Key(int a, int b) : sum(a b) {}
int sum;
bool operator<(const Key& other) const {
return sum < other.sum;
}
};
struct Value {
Value(const std::string& s, double d) : name(s), data(d) {}
std::string name;
double data;
};
int main() {
std::map<Key, Value> myMap;
// doesn't compile: ambiguous
// myMap.emplace(3, 4, "example", 42.0);
// works:
myMap.emplace(
std::piecewise_construct,
std::forward_as_tuple(3, 4),
std::forward_as_tuple("example", 42.0)
);
}
Fun with quadratic pack-expansions[9]
代码语言:javascript复制consteval bool all_of(const auto& f, const auto&... xs) {
return (f(xs) && ...);
}
consteval bool contains(const auto& n, const auto&... hs) {
return ((hs == n) || ...);
}
consteval int count(const auto& n, const auto&... hs) {
return (0 ... (hs == n));
}
static_assert(count(1, 3,1,4,1,6) == 2);
static_assert(count(2, 3,1,4,1,6) == 0);
这都很常规
类似的,可以写个判断重复
代码语言:javascript复制consteval bool has_any_duplicates() { return false; }
consteval bool has_any_duplicates(const auto& n, const auto&... hs) {
return ((n == hs) || ...) || has_any_duplicates(hs...);
}
static_assert(has_any_duplicate(3,1,4,1,6));
static_assert(!has_any_duplicate(2,7,1,8,3));
static_assert(has_any_duplicate(9,9,9));
static_assert(!has_any_duplicate(9));
static_assert(!has_any_duplicate());
代码语言:javascript复制
有点递归了,感觉和count有点像?用count改写
代码语言:javascript复制constexpr auto has_duplicate_of(const auto& value) {
return [&](const auto&... hs) {
return count(value, hs...) >= 2;
};
}
consteval bool has_any_duplicates(const auto&... hs) {
return (has_duplicate_of(hs)(hs...) || ...);
}
再简化一下,不用 has_duplicate_of
代码语言:javascript复制consteval bool has_any_duplicates(const auto&... hs) {
return ((count(hs, hs...) >= 2) || ...);
}
不用count再简化一下
代码语言:javascript复制consteval bool has_any_duplicates(const auto&... hs) {
return ([&](const auto& n) { return (0 ... (hs == n)) >= 2; }(hs) || ...);
}
还有优化空间! 参数n可以优化
代码语言:javascript复制consteval bool has_any_duplicates(const auto&... hs) {
return ([&,&n=hs]{ return (0 ... (hs == n)) >= 2; }() || ...);
}
A case in optimizing auto-vectorized code[10]
手把手教你写SIMD代码。看晕了 代码在这里https://github.com/oliora/habr-switches-perf-test
Inside STL: The unordered_map, unordered_set, unordered_multimap, and unordered_multiset[11]
hashtable的内存结构,基本就这样
代码语言:javascript复制struct hashtable{
using hint = std::list<payload>::iterator;
std::list<payload> list;
std::vector<hint> buckets;
};
开链在现在的硬件看来已经证明是缓存不友好的了,flatmap可以解决这一点
Inside STL: The deque, design[12]
可以这样理解
代码语言:javascript复制template<typename T>
struct simple_deque {
T* elements;
T* first;
T* last;
size_t capacity;
};
/*
first last
elements | ? | ? | ? | ? | 1 | 2 | 3 | ? |
size = last - first =3
cap = 8
*/
大概就是这样的结构 从中间往外展开,如果空间用光,就分配alloc一条新的数组,然后管理数组间的串联关系
Inside STL: The deque, implementation[13]
列举各种实现的差异
On the optimal bounds for integer division by constants[14]
看晕了
C 20 Dynamic Allocations at Compile-time[15]
老文章,看代码
代码语言:javascript复制consteval auto as_constant(auto value) {
return value;
}
consteval int Calc(int x) { return 4 * x; }
int main() {
auto res = Calc(2);
// auto res = as_constant(Calc(2));
res;
res = Calc(res); //编译不过
return res;
}
如果改成as_constant就能编译过。as_constant看起来傻逼,但是强制编译期计算了
Making your own array[16]
造轮子,没觉得有啥意思 代码这里 https://github.com/PipeRift/pipe/blob/feature/custom-arrays/Include/Pipe/PipeArrays.h
C 23: multidimensional operator[][17]
看代码, 多维数组访问下标
代码语言:javascript复制#include <vector>
#include <https://raw.githubusercontent.com/kokkos/mdspan/single-header/mdspan.hpp>
#include <iostream>
int main()
{
std::vector v = {1,2,3,4,5,6,7,8,9,10,11,12};
// View data as contiguous memory representing 2 rows of 6 ints each
auto multispan = std::experimental::mdspan(v.data(), 2, 6);
std::cout << multispan[0, 1] << 'n'; // 2
}
Passkey Idiom: A Useful Empty Class[18]
想让对象通过特别的factory来构造。自己不能构造
看代码。没啥说的,就是private tag类限制
tag类甚至可以不用
代码语言:javascript复制class Secret {
class ConstructorKey {
friend class SecretFactory;
private:
ConstructorKey() {};
ConstructorKey(ConstructorKey const&)
= default;
};
public:
//Whoever can provide a key has access:
explicit Secret(std::string str,
ConstructorKey) : data(std::move(str)) {}
private:
//these stay private, since Secret itself has
// no friends any more
void addData(std::string const& moreData);
std::string data;
};
class SecretFactory {
public:
Secret getSecret(std::string str) {
return Secret{std::move(str), {}};
}
// void modify(Secret& secret,
// std::string const& additionalData) {
// secret.addData(additionalData); //ERROR:
// // void Secret::addData(const string&)
// // is private
// }
};
int main() {
Secret s{"foo?", {}}; //ERROR:
// Secret::ConstructorKey::ConstructorKey()
// is private
SecretFactory sf;
Secret s = sf.getSecret("moo!"); //OK
}
The downsides of C Coroutines[19]
无栈协程各种缺点
- • 参数生命周期问题
task<void> async_insert(T && val);
task<void> async_find(const T &);
task<void> async_write(span<byte>);
这种不注意生命周期一不留神就用错
- • 迭代器安全
task<void> send_all(string s) {
for (auto & source : m_sources)
{
co_await source.send(s);
}
}
看上去没问题,如果m_sources有改动,就完了
所以复制一下是不是就没问题了?
代码语言:javascript复制task<void> send_all(string s)
{
std::vector<task<void>> sends;
sends.reserve(m_sources.size());
for (auto & source : m_sources)
{
sends.push_back(source->send(s));
}
co_await wait_all( sends.begin(), sends.end() );
}
等一下,m_sources如果析构了呢?
保活一下
代码语言:javascript复制task<void> send_all(string s)
{
std::vector<task<void>> sends;
std::vector<shared_ptr<Source>> sources = m_sources;
sends.reserve(sources.size());
for (auto & source : sources)
{
sends.push_back(source->send(s));
}
co_await wait_all( sends.begin(), sends.end() );
}
或者有个爹
代码语言:javascript复制struct Source
{
std::vector<std::coroutine_handle> dependent_coroutines;
~Source()
{
for (auto & coroutine : dependent_coroutines)
coro.destroy();
}
task<void> send(string s)
{
auto corohandle = co_await get_current_coroutine{};
dependent_coroutines.push_back( corohandle );
...
dependent_coroutines.erase( dependent_coroutines.find( corohandle ) );
}
}
或者加个锁?
代码语言:javascript复制task<void> send_all(string s)
{
co_await m_sourcesLock.read_lock();
std::vector<task<void>> sends;
sends.reserve(m_sources.size());
for (auto & source : m_sources)
{
sends.push_back(source->send(s));
}
co_await wait_all( sends.begin(), sends.end() );
co_await m_sourcesLock.read_unlock();
}
已经想吐了
- • 协程执行的可快可慢
co_await fetch_data("key");
这代码的问题相信你能看出来 key的生命周期是临时的。如果立即执行这个协程,不会崩,你显然没发现这个bug
然后这种代码越来越多,突然某一天就崩了,你还在纳闷咋回事
哦你发现了,想了想,保存一下,延长一下生命周期,应该行了吧
代码语言:javascript复制eager_task<string> fetch_data(string_view key)
{
auto it = cache.find(key)
if (it != cache.end())
{
return it->second;
}
auto data = co_await fetch_remote(key);
cache.emplace(key, data);
}
但是指不定某个兄弟就这么写了
代码语言:javascript复制eager_task<void> fetch_mydata(string_view key)
{
return fetch_data(std::format("mysystem/{}", key));
}
同样的崩溃问题又出现了
- • 测试问题,怎么避免上面这种用法的出现?await_transform?
- • 性能问题,内存分配的浪费?即使有HALO优化,还是会有很大的栈空间浪费?
- • 还是性能问题,生成的函数太多了,debug build很多浪费,这个无解,只能等编译器优化
视频
- • A Graph Based Update System in C : Lightning Updates - Hana Dusíková [20]
讲的玩意类似taskflow,但是是用hash来串联类型依赖。没有源代码
- • Function Contracts in Practice using C - Rostislav Khlebnikov - ACCU 2023[21]
讲函数怎么写assert写约束写断言,还算有点意思,周末整理一下这个
开源项目需要人手
- • asteria[22] 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群384042845和作者对线
- • Unilang[23] deepin的一个通用编程语言,点子有点意思,也缺人,感兴趣的可以github讨论区或者deepin论坛看一看。这里也挂着长期推荐了
- • gcc-mcf[24] 懂的都懂
新项目介绍/版本更新
- • https://github.com/SpartanJ/efsw filewatch 基于inotify
- • https://github.com/mpusz/mp-coro 一个协程库
- • https://github.com/cppalliance/mrdox 一个文档生成工具
工作招聘
有没有数据库相关的工作推荐我一下,我要失业了快
本文永久链接[25]
如果有疑问评论最好在上面链接到评论区里评论,这样方便搜索,微信公众号有点封闭/知乎吞评论
引用链接
[1]
周刊项目地址: https://github.com/wanghenshui/cppweeklynews
[2]
提交 issue: https://github.com/wanghenshui/cppweeklynews/issues
[3]
cppwinrt is in maintenance mode: https://github.com/microsoft/cppwinrt/issues/1289#issuecomment-1481303844
[4]
Did you know that C 26 added 'A nice placeholder with no name' : https://github.com/tip-of-the-week/cpp/blob/master/tips/342.md
[5]
std::ignore: https://en.cppreference.com/w/cpp/utility/tuple/ignore
[6]
Inside STL: The lists: https://devblogs.microsoft.com/oldnewthing/20230804-00/?p=108547
[7]
Inside STL: The map, set, multimap, and multiset: https://devblogs.microsoft.com/oldnewthing/20230807-00/?p=108562
[8]
Five Advanced Initialization Techniques in C : From reserve() to piecewise_construct and More.: https://www.cppstories.com/2023/five-adv-init-techniques-cpp/
[9]
Fun with quadratic pack-expansions: https://quuxplusone.github.io/blog/2023/08/05/quadratic-pack-expansions/
[10]
A case in optimizing auto-vectorized code: https://oliora.github.io/2023/08/07/Optimizing-auto-vectorized-code.html
[11]
Inside STL: The unordered_map, unordered_set, unordered_multimap, and unordered_multiset: https://devblogs.microsoft.com/oldnewthing/20230808-00/?p=108572
[12]
Inside STL: The deque, design: https://devblogs.microsoft.com/oldnewthing/20230809-00/?p=108577
[13]
Inside STL: The deque, implementation: https://devblogs.microsoft.com/oldnewthing/20230810-00/?p=108587
[14]
On the optimal bounds for integer division by constants: https://jk-jeon.github.io/posts/2023/08/optimal-bounds-integer-division/
[15]
C 20 Dynamic Allocations at Compile-time: https://accu.org/journals/overload/31/176/fertig/
[16]
Making your own array: https://muit.xyz/posts/making-your-own-array/
[17]
C 23: multidimensional operator[]: https://www.sandordargo.com/blog/2023/08/09/cpp23-multidimensional-subscription-operator
[18]
Passkey Idiom: A Useful Empty Class: https://accu.org/journals/overload/31/176/mertz/
[19]
The downsides of C Coroutines: https://reductor.dev/cpp/2023/08/10/the-downsides-of-coroutines.html
[20]
A Graph Based Update System in C : Lightning Updates - Hana Dusíková : https://www.youtube.com/watch?v=C9MWAXYdFSY&ab_channel=CppNow
[21]
Function Contracts in Practice using C - Rostislav Khlebnikov - ACCU 2023: https://www.youtube.com/watch?v=5ttyA1-I8D8&ab_channel=ACCUConference
[22]
asteria: https://github.com/lhmouse/asteria
[23]
Unilang: https://github.com/linuxdeepin/unilang
[24]
gcc-mcf: https://gcc-mcf.lhmouse.com/
[25]
本文永久链接: https://wanghenshui.github.io/cppweeklynews/posts/126.html