layout | title |
---|---|
post |
第127期 |
公众号
RSS https://github.com/wanghenshui/cppweeklynews/releases.atom
欢迎投稿,推荐或自荐文章/软件/资源等
内容不多
标准委员会动态/ide/编译器信息放在这里
编译器信息最新动态推荐关注hellogcc公众号 本周更新 2023-08-16 第215期
boost 1.8.3发布,最后一个支持c++03的版本
https://www.boost.org/users/history/version_1_83_0.html
重点发布就是boost::concurrent_flat_map
Unordered:
Major update.
Added boost::concurrent_flat_map, a fast, thread-safe hashmap based on open addressing.
Sped up iteration of open-addressing containers.
In open-addressing containers, erase(iterator), which previously returned nothing, now returns a proxy object convertible to an iterator to the next element. This enables the typical it = c.erase(it) idiom without incurring any performance penalty when the returned proxy is not used.
int main() {
auto i = 42;
std::cout << std::format("{:#018X}", reinterpret_cast<uintptr_t>(&i)); // prints 0X00007FFD9D71776C
}
有点用,但也不是很多
asio::const_buffer b5 = 0xaB_buf;
ASIO_CHECK(b5.size() == 1);
ASIO_CHECK(memcmp(b5.data(), "\xab", 1) == 0);
asio::const_buffer b6 = 0xABcd_buf;
ASIO_CHECK(b6.size() == 2);
ASIO_CHECK(memcmp(b6.data(), "\xab\xcd", 2) == 0);
asio::const_buffer b7 = 0x01ab01cd01ef01ba01dc01fe_buf;
ASIO_CHECK(b7.size() == 12);
ASIO_CHECK(memcmp(b7.data(),
"\x01\xab\x01\xcd\x01\xef\x01\xba\x01\xdc\x01\xfe", 12) == 0);
编译期hex转字符串。很妙
代码在这里 https://github.com/chriskohlhoff/asio/blob/master/asio/include/asio/buffer.hpp#L2743
shared_ptr有额外的计数信息, 大概这样
struct control_block {
virtual void Dispose() = 0;
virtual void Delete() = 0;
std::atomic<unsigned long> shareds;
std::atomic<unsigned long> weaks;
};
template<typename T>
struct shared_ptr {
T* object;
control_block* control;
};
template<typename T>
struct weak_ptr {
T* object;
control_block* control;
};
unique_ptr比较简单,就是指针+deleter,然后空基类优化(compressed_pair)
讲智能指针的内存布局
shared_ptr是有额外的信息的,这部分信息需要一个分配
auto p = std::shared_ptr<S>(new S());
auto p = std::make_shared<S>();
这两种构造,第一种,由于是接管,S的内存和shared_ptr内部信息不是连续的,这对局部性缓存是不友好的
enable_shared_from_this怎么实现的?大概思路就是weak_ptr
template<typename T>
struct enable_shared_from_this {
using esft_detector = enable_shared_from_this;
std::weak_ptr<T> weak_this;
std::weak_ptr<T> weak_from_this()
{ return weak_this; }
std::shared_ptr<T> shared_from_this()
{ return weak_this.lock(); }
};
weak_this由谁来赋值?肯定是shared_ptr拉
template<typename T, typename D>
struct shared_ptr {
shared_ptr(T* ptr)
{
... do the usual stuff ...
/* Here comes enable_shared_from_this magic */
if constexpr (supports_esft<T>::value) {
using detector = T::esft_detector;
ptr->detector::weak_this = *this;
}
}
... other constructors and stuff ...
};
只要enable_shared_from_this实现 esft_detector就行了,类似这样
template<typename T, typename = void>
struct supports_esft : std::false_type {};
template<typename T>
struct inline bool supports_esft<T,
std::void_t<typename T::esft_detector>>
: std::true_type {};
这样继承的类都有特化的shared_ptr构造
编译器还没加上这个能力,可以用这个体验 https://github.com/kokkos/mdspan,在线 https://godbolt.org/z/Mxa7cej1a
之前也讲过很多次了,直接贴代码吧
std::array numbers {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
stdex::mdspan<int, stdex::extents<int, 2, 5>, stdex::layout_right> mdspanOfNumbers {numbers.data()};
for (size_t rowIndex=0; rowIndex < mdspanOfNumbers.extent(0); ++rowIndex) {
for (size_t columnIndex=0; columnIndex < mdspanOfNumbers.extent(1); ++columnIndex) {
std::cout << mdspanOfNumbers[rowIndex, columnIndex] << ' ';
}
std::cout << '\n';
}
/*
1 2 3 4 5
6 7 8 9 10
*/
std::array numbers {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
stdex::mdspan<int, stdex::extents<int, 2, 5>, stdex::layout_left> mdspanOfNumbers {numbers.data()};
for (size_t columnIndex=0; columnIndex < mdspanOfNumbers.extent(0); ++columnIndex) {
for (size_t rowIndex=0; rowIndex < mdspanOfNumbers.extent(1); ++rowIndex) {
std::cout << mdspanOfNumbers[columnIndex, rowIndex] << ' ';
}
std::cout << '\n';
}
/*
1 3 5 7 9
2 4 6 8 10
*/
SIMD时间
常规
uint8_t leading_byte = data[pos]; // leading byte
if (leading_byte < 0b10000000) {
*latin_output++ = leading_byte;
pos++;
} else if ((leading_byte & 0b11100000) == 0b11000000) {
*latin_output++ = (leading_byte & 1) << 6 | (data[pos + 1]);
pos += 2;
}
simd
__m512i input = _mm512_loadu_si512((__m512i *)(buf + pos));
__mmask64 leading = _mm512_cmpge_epu8_mask(input, _mm512_set1_epi8(-64));
__mmask64 bit6 = _mm512_mask_test_epi8_mask(leading, input, _mm512_set1_epi8(1));
input = _mm512_mask_sub_epi8(input, (bit6<<1) | next_bit6, input, _mm512_set1_epi8(-64));
next_bit6 = bit6 >> 63;
__mmask64 retain = ~leading;
__m512i output = _mm512_maskz_compress_epi8(retain, input);
int64_t written_out = _popcnt64(retain);
__mmask64 store_mask = (1ULL << written_out) - 1;
_mm512_mask_storeu_epi8((__m512i *)latin_output, store_mask, output);
完整代码 https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2023/08/11
range的坑,没有完全零开销,需要拷贝
手把手教你写协程
他也写了个range库 flux,对比了一下和range的差异,优缺点
推荐使用magic_enum
CppNow基本上一周出几个视频,列个有意思的
ppt在这里 https://www.jonathanmueller.dev/talk/think-cell-library/
代码在这里 https://github.com/think-cell/think-cell-library
非常通用的组件介绍
比如std::exchange
template <typename T>
class my_smart_ptr {
T* _ptr;
public:
my_smart_ptr(my_smart_ptr&& other) noexcept
: _ptr(other._ptr) {
other._ptr = nullptr;
}
};
用上exchange
template <typename T>
class my_smart_ptr {
T* _ptr;
public:
my_smart_ptr(my_smart_ptr&& other) noexcept
: _ptr(std::exchange(other._ptr, nullptr))
{}
};
这种语义是upsert,不只指针,其他value也可以这样优化,代码干净
所以实现了tc::change
void tc::optional<T>::reset() {
if (_has_value) {
_has_value = false;
value().~T();
}
}
使用tc::change
void tc::optional<T>::reset() {
if (tc::change(_has_value, false)) {
value().~T();
}
}
为啥为了省一个if这么麻烦?,举个例子,异常+重入
void foo1() {
…
if (dirty) {
clean();
dirty = false;
}
…
}
void foo2() {
…
if (tc::change(dirty, false)) {
try {
clean();
} catch (...) {
dirty = true;
throw;
}
}
…
}
foo2比foo1更健壮点,避免了同时clean的场景
假如多个线程都在clean,而clean时间较长,dirty更新不及时,就更新了多次
foo2就避免了这种情况
还有一些range string的就不列了,感兴趣的可以看看
- asteria 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群753302367和作者对线
- Unilang deepin的一个通用编程语言,点子有点意思,也缺人,感兴趣的可以github讨论区或者deepin论坛看一看。这里也挂着长期推荐了
- gcc-mcf 懂的都懂
如果有疑问评论最好在上面链接到评论区里评论,这样方便搜索,微信公众号有点封闭/知乎吞评论