C++ 中文周刊 第127期

周刊项目地址

公众号

RSS https://github.com/wanghenshui/cppweeklynews/releases.atom

欢迎投稿,推荐或自荐文章/软件/资源等

提交 issue

内容不多


资讯

标准委员会动态/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.

文章

Did you know that C++26 std.format added formatting pointers ability

int main() {
    auto i = 42;
    std::cout << std::format("{:#018X}", reinterpret_cast<uintptr_t>(&i)); // prints 0X00007FFD9D71776C
}

有点用,但也不是很多

模板元编程的精妙之–2/16进制字面量转换为编译期字符串

  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

Inside STL: Smart pointers

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)

Inside STL: The shared_ptr constructor vs make_shared

讲智能指针的内存布局

shared_ptr是有额外的信息的,这部分信息需要一个分配

auto p = std::shared_ptr<S>(new S());
auto p = std::make_shared<S>();

这两种构造,第一种,由于是接管,S的内存和shared_ptr内部信息不是连续的,这对局部性缓存是不友好的

Inside STL: The shared_ptr constructor and enable_shared_from_this

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构造

C++23: mdspan

编译器还没加上这个能力,可以用这个体验 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 
*/

Transcoding UTF-8 strings to Latin 1 strings at 18 GB/s using AVX-512

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

Some C++20 ranges aren’t const-iterable

range的坑,没有完全零开销,需要拷贝

Writing custom C++20 coroutine systems

手把手教你写协程

Parameter Passing in Flux versus Ranges

他也写了个range库 flux,对比了一下和range的差异,优缺点

How to convert an enum to string in C++

推荐使用magic_enum

视频

CppNow基本上一周出几个视频,列个有意思的

The New C++ Library: Strong Library Foundation for Future Projects - Jonathan Müller & Arno Schödl

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的就不列了,感兴趣的可以看看

开源项目需要人手

工作招聘

有没有数据库相关的工作推荐我一下,我要失业了快(能远程更好)

本文永久链接

如果有疑问评论最好在上面链接到评论区里评论,这样方便搜索,微信公众号有点封闭/知乎吞评论

看到这里或许你有建议或者疑问或者指出错误,请留言评论! 多谢! 你的评论非常重要!也可以帮忙点赞收藏转发!多谢支持! 觉得写的不错那就给点吧, 在线乞讨 微信转账