C++ 中文周刊 第143期

周刊项目地址

公众号

qq群 点击进入

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

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

提交 issue 或者评论区留言

另外公众号挂了c++templates 第二版优惠

从上面的链接里下单的兄弟买书到货后可以找我退佣金,加我微信,公众号后台回复即可

本期文章由 黄亮 不语 赞助


资讯

标准委员会动态/ide/编译器信息放在这里

编译器信息最新动态推荐关注hellogcc公众号 本周更新 2023-12-20 第233期

委员会邮件 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/#mailing2023-12

本月委员会邮件没有什么新鲜的,顶多fiber_context。这里不展开了

文章

Did you know that C++26 added Pack Indexing?

template<auto N> consteval auto nth(auto... ts) { return ts...[N]; }
static_assert(1 == nth<0>(1, 2, 3));
static_assert(2 == nth<1>(1, 2, 3));
static_assert(3 == nth<2>(1, 2, 3));

还不确定有什么作用

The double life of objects

const的副作用

经典例子

#include <iostream>
int main() {
  const int i = 9;
  int& j = const_cast<int&>(i);
  j = 4;
  std::cout << i << std::endl; // prints 9
  std::cout << j << std::endl; // prints 4
}

https://godbolt.org/z/vGG3cdavE

但是这个例子,返回优化了,即使没有实现move

#include <iostream>
#include <cassert>

class Rng {
    int _min;
    int _max;
    // invariant: _min <= _max

public:
    Rng(int lo, int hi) 
    // precond: lo <= hi
    : _min(lo), _max(hi)
    { assert(_min <= _max); }

    int const& min() const { return _min; }
    int const& max() const { return _max; }

    void set(int lo, int hi)
    // precond: lo <= hi
    {
        _min = lo;
        _max = hi;
        assert(_min <= _max); 
    }

    Rng(Rng&&) { assert(false); } // this is never called
    Rng(Rng const&) { assert(false); } // this is never called
};

const Rng foo() {
  const Rng r {1, 2};
  std::cout << &r << std::endl;
  return r;
}

Rng bar() {
    Rng r = foo();
    r.set(3, 4);
    std::cout << &r << std::endl;
    return r;
}

int main() {
    const Rng z = bar();
    std::cout << &z << std::endl;
}

https://godbolt.org/z/n9nn5GjMM

注意这两个例子的区别,统一作用域上的修改

上面的这个xyz 本质上就是一个对象,和第一个例子同一个域里const_cast导致变化不同

About time - how to unit test code that depends on time

怎么mock时间?比如特化?

template <typename ...>
constexpr auto clock_impl = std::chrono::some_clock{};

template <typename ... Ts>
struct app_clock {
    static
    std::chrono::some_clock::time_point now()
    {
        return clock_impl<Ts...>.now();
    }
};
struct test_clock {
    using time_point = std::chrono::some_clock::time_point;
    static time_point now() { return {};}
};

template <>
constexpr auto clock_impl<> = test_clock{};

https://godbolt.org/z/GbWYaGc7q

浮点数误差入门

讲的不错

linux kernel list 为什么用WRITE_ONCE?

写的很有深度,值得一看

从一个crash问题展开,探索gcc编译优化细节

省流 arm O3 优化bug

Trivial Auto Var Init Experiments

-ftrivial-auto-var-init=[pattern|zero|uninitialized]

帮助自动初始化栈上的局部变量

开销很大,研究了一圈放弃了

Two kinds of function template parameters

一种是make_unique这种需要指定T的,一种是swap sort这种不指定T的

如何跨过这种边界,有设计,比如CTAD,但这并不建议使用

那就只能多提供重载了,比如optional

template<class T, class A>
optional<T> make_optional(A);
template<class A>
optional<A> make_optional(A);

然后她举了个例子,怎么设计强制制定T和忽略T

https://godbolt.org/z/h38PhG3Y6

#include <type_traits>
#include <iostream>

//template<class T, class A>
//T implicitly_convert_to(std::type_identity_t<A>) = delete;

template<class T, class A,
         std::enable_if_t<std::is_convertible_v<A, T>, int> E = 0>
T implicitly_convert_to(A arg) { return T(arg); }

int main() {
  //auto i0 = implicitly_convert_to(9.9999999);
  //std::cout << i0 << "\n";
  auto i1 = implicitly_convert_to<int>(9.9999999);
  std::cout << i1 << "\n";

  //auto j2 = implicitly_convert_to<int, float>(9.9999999);
  //std::cout << j2 <<"\n";
  return 0;
}

看一乐

A Coroutines-Based Single Consumer – Single Producer Workflow by Ljubic Damir

直接贴代码了

https://godbolt.org/z/MvYfbEP8r

https://godbolt.org/z/57zsK9rEn

设计的挺有意思的,鉴于篇幅,放在后面

手动优化C++代码来加快编译速度?!

constexpr的代码 编译器没有做充分的优化。这可能加剧编译时长

算是个坑爹细节。运行时能充分优化的代码到了constexpr期反而没优化了

Raymond windows环节,看不懂

视频

Cache-friendly Design in Robot Path Planning with C++ - Brian Cairl - CppCon 2023

寻路算法,A*之类的,如何缓存友好。STL不太行

valgrind 也可以测试cache性能,判断miss

valgrind --tool=cachegrind --cache-sim=yes

perf也可以,就不说了

结论就是 顺序访问 不要跳转 只访问用到的数据 s执行路径里没有malloc

比如std::unordered_multimap::equal_range 内存不连续,miss就很多

"Distributed Ranges": Model for Building Distributed Data Structures, Algorithms & Views - Ben Brock

概念很帅,把range推广到分布式,做的一些工作

代码在这里 https://github.com/oneapi-src/distributed-ranges/tree/main

开源项目推荐

代码段

#include <iostream>
#include <vector>
#include <coroutine>
#include <chrono>
#include <thread>
#include <utility>
#include <functional>
#include <memory>
#include <algorithm>
#include <iterator>
#include <atomic>

#define FUNC() std::cout << __func__ << '\n'

namespace details {
    template <typename InputIterator>
    void printIterable(InputIterator first, InputIterator last) {
        using value_type = std::decay_t<decltype(*first)>;
        std::cout << '[';
        if constexpr (std::is_same_v<std::uint8_t, value_type>) {
            std::copy(first, std::prev(last), std::ostream_iterator<std::uint16_t>(std::cout, ", "));
            std::cout << static_cast<std::uint16_t>(*std::prev(last)) << "]\n";
        } else {
            std::copy(first, std::prev(last), std::ostream_iterator<value_type>(std::cout, ", "));
            std::cout << *std::prev(last) << "]\n";
        }
    }

    template <typename Container>
    void printContainer(const Container& container) {
        printIterable(std::cbegin(container), std::cend(container));
    }
}

class [[nodiscard]] AudioDataResult final {
    public:
        class promise_type;
        using handle_type = std::coroutine_handle<promise_type>;
      
        // Predefined interface that has to be specify in order to implement
        // coroutine's state-machine transitions
        class promise_type {  
            public:     
                using value_type = std::vector<int>;

                AudioDataResult get_return_object() {
                    return AudioDataResult{handle_type::from_promise(*this)};
                }
                std::suspend_never initial_suspend() noexcept { return {}; }
                std::suspend_always final_suspend() noexcept { return {}; }
                void return_void() {}
                void unhandled_exception() {
                    std::rethrow_exception(std::current_exception());
                }

                // Generates the value and suspend the "producer"
                template <typename Data>
                requires std::convertible_to<std::decay_t<Data>, value_type>
                std::suspend_always yield_value(Data&& value) {
                    data_ = std::forward<Data>(value);
                    data_ready_.store(true, std::memory_order_relaxed);
                    return {};
                }

                auto await_transform(handle_type other) {
                    // Awaiter interface: for consumer waiting on data being ready
                    struct AudioDataAwaiter {
                        explicit AudioDataAwaiter(promise_type& promise) noexcept: promise_(promise) {}

                        bool await_ready() const { return promise_.data_ready_.load(std::memory_order_relaxed);}
                      
                        void await_suspend(handle_type) const {
                            while(not promise_.data_ready_.exchange(false)) {
                                std::this_thread::yield(); 
                            }
                        }
                      
                        value_type&& await_resume() const {
                            return std::move(promise_.data_);
                        }

                        private: 
                            promise_type& promise_;
                    };//Awaiter interface

                    return AudioDataAwaiter{other.promise()};
                }      
            private:
                value_type data_;
                std::atomic<bool> data_ready_;
        }; //promise_type interface

        explicit operator handle_type() const { return handle_;}

        // Make the result type move-only, due to ownership over the handle
        AudioDataResult(const AudioDataResult&) = delete;
        AudioDataResult& operator=(const AudioDataResult&) = delete;

        AudioDataResult(AudioDataResult&& other) noexcept: handle_(std::exchange(other.handle_, nullptr)){}
        AudioDataResult& operator=(AudioDataResult&& other) noexcept {
            using namespace std;
            AudioDataResult tmp = std::move(other);
            swap(*this, tmp);
            return *this;
        }

        // d-tor: RAII
        ~AudioDataResult() { if (handle_) {FUNC(); handle_.destroy();}}

        // For resuming the producer - at the point when the data are consumed
        void resume() {if (not handle_.done()) { FUNC(); handle_.resume();}}

          
    private:
        AudioDataResult(handle_type handle) noexcept : handle_(handle) {}

    private:
    handle_type handle_;
};


using data_type = std::vector<int>;
AudioDataResult producer(const data_type& data) {
    for (std::size_t i = 0; i < 5; ++i) {
        FUNC();
        co_yield data;
    }
    co_yield data_type{}; // exit criteria

    co_return;
}

AudioDataResult consumer(AudioDataResult& audioDataResult) {
    for(;;) {
        FUNC();
        const auto data = co_await static_cast<AudioDataResult::handle_type>(audioDataResult);
        if (data.empty()) {std::cout << "No data - exit!\n"; break;}
        std::cout << "Data received:";
        details::printContainer(data);

        audioDataResult.resume(); // resume producer
    }
    co_return;
}

int main() {
    {
        const data_type data = {1, 2, 3, 4};
        auto audioDataProducer = producer(data);
        std::thread t ([&]{auto audioRecorded = consumer(audioDataProducer);});
        t.join();
    }

    std::cout << "bye-bye!\n";
    return 0;
}

互动环节

最近看 丁胖子金牌讲师视频,非常乐呵,讲师也是学c++ 的

啥也不是,散会!


本文永久链接

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

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