C++ 中文周刊 第6期

reddit/hackernews/lobsters/meetingcpp摘抄一些c++动态。

每周更新

周刊项目地址 github在线地址知乎专栏

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


资讯

编译器信息最新动态推荐关注hellogcc公众号

本周周报github直达

文章

作者描述了一个move对象,但对象没有实现好拷贝函数和移动函数导致UB的场景。在 -stdlib=libc++的某些场景下的复制可能出现问题。我测试了没复现出来

使用整数上的注意事项,讲了很多,包括整数提升相关的细节

这里列出笔者比较感兴趣的

  1. 溢出
    1. 比如符号数溢出,比如UINT_MAX + 1 == 0.
    2. 溢出和提升规则结合导致的bug
    3. 除法溢出 INT_MIN / -1.
    4. 位移与溢出结合的问题
  2. 循环计数
    1. 条件比较 符号不一样,可能死循环 编译器会警告

Raymond写文章非常多 这个就是介绍一个promise type的封装。他也写了很多coroutine文章。写的很琐碎,后面一系列文章都是围绕这个封装做组件库。就不多介绍了

如果真的想用库,用这个 https://github.com/lewissbaker/cppcoro

观点没什么意思,就是照搬 https://github.com/mtrebi/memory-allocators的readme,值得看看

简单来说,内存分配器要有自己的定义和选型,比如应用的数据是可以预估的proxy,数据失效快,那就可以分配一个大数组,固定块,来分配,在程序一开始就分配好,之后就不用malloc,全部placement new就可以

如果是比较琐碎的数据类型,变化比较多,那还是jemalloc这种更合适一些

看这段代码,演示在这里https://godbolt.org/z/jEGK1z3nT

class iapi {
 public:
  virtual ~iapi() = default;
  virtual auto call() const -> int = 0;
};

template<class T>
concept api = requires(const T& t) {
  { t.call() } -> std::same_as<int>;
};

template<class T1 = class iapi, // NOTE: class iapi for template injection
           api T2 = class iapi> // NOTE: class iapi for concepts injection
struct app {
  constexpr app(const T1& t1, const T2& t2, const iapi& t3) { // NOTE: iapi for interface injection
    assert(42 == t1.call() and 42 == t2.call() and 42 == t3.call());
  }
};

int main() {
  struct fake_api : iapi {
    auto call() const -> int { return 42; }
  };

  const auto injector = boost::di::make_injector(
    boost::di::bind<iapi>.to<fake_api>() // bind iapi to fake_api
  );

  boost::di::create<app>(injector); // return an app
}

还是生成器,co_yield需要实现promise_type的五个函数,就行了,不用实现awaitable

看代码

#include <coroutine>
#include <memory>
#include <iostream>
#include <string>
#include <vector>

template<typename T>
struct Generator {
    
    struct promise_type;
    using handle_type = std::coroutine_handle<promise_type>;
    
    Generator(handle_type h): coro(h) {}                      

    handle_type coro;
    
    ~Generator() {  
        if ( coro ) coro.destroy();
    }
    Generator(const Generator&) = delete;
    Generator& operator = (const Generator&) = delete;
    Generator(Generator&& oth): coro(oth.coro) {
        oth.coro = nullptr;
    }
    Generator& operator = (Generator&& oth) {
        coro = oth.coro;
        oth.coro = nullptr;
        return *this;
    }
    T getNextValue() {
        coro.resume();
        return coro.promise().current_value;
    }
    struct promise_type {
        promise_type() {}                              
          
        ~promise_type() {}
        
        std::suspend_always initial_suspend() {            
            return {};
        }
        std::suspend_always final_suspend() noexcept {
            return {};
        }
        auto get_return_object() {      
            return Generator{handle_type::from_promise(*this)};
        }
        // co_yeilds
        std::suspend_always yield_value(const T value) {    
            current_value = value;
            return {};
        }
        void return_void() {}
        void unhandled_exception() {
            std::exit(1);
        }

        T current_value;
    };
};

template <typename Cont>
Generator<typename Cont::value_type> getNext(Cont cont) {
    for (auto c: cont) co_yield c;
}

int main() {

    std::cout << '\n';
  
    std::string helloWorld = "Hello world";
    auto gen = getNext(helloWorld);                        // (1)
    for (int i = 0; i < helloWorld.size(); ++i) {
        std::cout << gen.getNextValue() << " ";            // (4)
    }

    std::cout << "\n\n";

    auto gen2 = getNext(helloWorld);                       // (2)
    for (int i = 0; i < 5 ; ++i) {                         // (5)
        std::cout << gen2.getNextValue() << " ";
    }

    std::cout << "\n\n";

    std::vector myVec{1, 2, 3, 4 ,5};
    auto gen3 = getNext(myVec);                           // (3)
    for (int i = 0; i < myVec.size() ; ++i) {             // (6)
        std::cout << gen3.getNextValue() << " ";
    }
    
    std::cout << '\n';

}

编译器会把coroutine改成两个流程 promise流程和awaitable流程

promise

{
    Promise prom;
    co_await prom.initial_suspend();
    try {
        <function body having co_return, co_yield, or co_wait>
    }
    catch (...) {
        prom.unhandled_exception();
    }
FinalSuspend:
    co_await prom.final_suspend();
}

awaitable

awaitable.await_ready() returns false:
    
    suspend coroutine
	
    awaitable.await_suspend(coroutineHandle) returns: 
	
        void:
            awaitable.await_suspend(coroutineHandle);
            coroutine keeps suspended
            return to caller

        bool:
            bool result = awaitable.await_suspend(coroutineHandle);
            if result: 
                coroutine keep suspended
                return to caller
            else: 
                go to resumptionPoint

        another coroutine handle:	
            auto anotherCoroutineHandle = awaitable.await_suspend(coroutineHandle);
            anotherCoroutineHandle.resume();
            return to caller
	
resumptionPoint:

return awaitable.await_resume();

视频

对于基本类型的转换,不要用reinterpret_cast了!用bit_cast!gcc trunk分支已经支持了,可以在godbolt上玩一玩

ppt https://ciura.ro/presentations/2020/Conferences/Symbolism%20-%20Rainbows%20and%20Crashes%20-%20Victor%20Ciura%20-%20Meeting%20C++%202020.pdf

window平台抓崩溃信息的一个总结回顾


本文永久链接

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