C++ 中文周刊 第80期

reddit/hackernews/lobsters/meetingcpp知乎等等摘抄一些c++动态

周刊项目地址在线地址知乎专栏 腾讯云+社区

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

可以贴在下一期草稿里 草稿链接

2022 0916


资讯

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

编译器信息最新动态推荐关注hellogcc公众号 2022-09-14 第167期

cppcon 2022还在进行中。视频放了几个。没看。有参与者说今年不太行。我去年还没怎么看。看视频信息量太低了。得看PPT然后找视频看。最近没啥空。周末有时间可以看看

文章

打印堆栈支持。看代码

#include <stacktrace>
#include <iostream>

int foo() {
  std::cout << std::stacktrace::current();
  return {};
}

int main() {
  return foo();
}

可算支持了

各个编译器Modules支持的状态进展介绍

标准库的std::optional是不支持T&和void的。作者说了下自己可能会用到这个场景,标准库应该加上

之前介绍过。介绍了谷歌浏览器团队在解决指针问题的一些实践。实现了很多,但是有些文档不可见。

也介绍了其他方案的实现,比如这个unowned_ptr

代码难找就没有深入研究。不过讨论还是值得一看的。我后面整理一下

介绍Sanitizer

seastar代码走读

注意co_await的阻塞语义,永远超时导致不能使用

四舍五入等于c的东西。可以看个热闹

各种容器介绍,比如folly::fbvector, boost::small_vector等等。感兴趣的可以看看

写了个工具验证gcc产出的代码对不对,具体就是验证gimple IR。我不太懂。感兴趣可以看看

office团队要用module,这是msvc团队的一些探索

介绍msvc调试的。没用过不了解,有没有懂的给讲讲

代码长这样

struct WidgetTracker : IWidgetChangeNotificationSink
{
    /* other stuff not relevant here */

    /// IWidgetChangeNotificationSink
    STDMETHODIMP OnCurrentWidgetChanged();

private:
    WRL::ComPtr<IWidget> m_currentWidget;
    std::mutex m_mutex;
};

HRESULT WidgetTracker::OnCurrentWidgetChanged()
{
    auto guard = std::lock_guard(m_mutex);
    RETURN_IF_FAILED(GetCurrentWidget(&m_currentWidget));
    return S_OK;
}

OnCurrentWidgetChanged是个回调,如果m_currentWidget变了就会被调用

问题在于OnCurrentWidgetChanged被调用这个m_mutex锁住的瞬间,m_currentWidget析构,结果又触发一次OnCurrentWidgetChanged,导致死锁

这个问题根源在于锁锁不住comptr,无法避免递归调用。

解决方法也很简单,拷贝一份对象就行了。

HRESULT WidgetTracker::OnCurrentWidgetChanged()
{
    WRL::ComPtr<IWidget> widget; // 可能别人在用它
    auto guard = std::lock_guard(m_mutex);
    RETURN_IF_FAILED(GetCurrentWidget(&widget));
    m_currentWidget.Swap(widget);
    return S_OK;
}

作者之前讨论过一段类似的代码,用的,shared_ptr, 也有相同的问题

class ThingManager
{
private:
  std::mutex things_lock_;
  std::vector<std::shared_ptr<Thing>> things_;

public:
  void AddThing(std::shared_ptr<Thing> thing)
  {
    std::lock_guard guard(things_lock_);
    things_.push_back(std::move(thing));
  }

  void RemoveThingById(int32_t id)
  {
    std::lock_guard guard(things_lock_);
    auto it = std::find_if(things_.begin(), things_.end(),
      [&](auto&& thing)
      {
        return thing->id() == id;
      });
    if (it != things_.end()) {
      things_.erase(it);
    }
  }
};

class SuperThing : Thing
{
private:
  ThingManager& manager_;
  int32_t helper_id_ = 0;

public:
  SuperThing(ThingManager& manager) :
    manager_(manager)
  {
    auto helper = std::make_shared<Thing>();
    helper_id_ = helper->id();
    manager_.AddThing(helper);
  }

  ~SuperThing()
  {
    manager_.RemoveThingById(helper_id_);
  }
};

void test(ThingManager& manager)
{
  auto s = std::make_shared<SuperThing>(manager);
  auto id = s->id();
  manager.AddThing(s);
  s = nullptr; // 1

  manager.RemoveThingById(id); // 2
}

问题是相同的,同一个锁被锁两次。如何触发?首先SuperThing会在2 这行真正的析构,1那行只会引用计数-1 RemoveThingById(id)是会锁的,内部触发了SuperThing析构,然后又调用了manager_.RemoveThingById(helper_id_);,锁了同一个锁

你可能觉得这种代码写的有问题。这是锁和shared_ptr和坑爹析构三个同时引入引发的问题,我遇不到

解决方法也很简单。让这个shared_ptr活着,因为不知道哪个外部调用会用到这个shared_ptr

void RemoveThingById(int32_t id)
{
  std::shared_ptr removed_thing; // 求求你活着
  {
    std::lock_guard guard(things_lock_);
    auto it = std::find_if(things_.begin(), things_.end(), ...);
    if (it != things_.end()) {
      removed_thing = *it;
      things_.erase(it);
    }
  }
}

引用计数指针和锁的问题。这里打个问号。要注意

oldnewthing的博客真精彩,总能遇到各种莫名其妙的bug

标准库提供了新函数来更友好的判断整数大小,不用自己写那些符号转换逻辑了

比如

template <class _Ty1, class _Ty2>
_NODISCARD constexpr bool cmp_equal(const _Ty1 _Left, const _Ty2 _Right) noexcept {
  static_assert(_Is_standard_integer<_Ty1> && _Is_standard_integer<_Ty2>,
   "The integer comparison functions only "
   "accept standard and extended integer types.");
  if constexpr (is_signed_v<_Ty1> == is_signed_v<_Ty2>) {
    return _Left == _Right;
  } else if constexpr (is_signed_v<_Ty2>) {
    return _Left == static_cast<make_unsigned_t<_Ty2>>(_Right) && _Right >= 0;
  } else {
    return static_cast<make_unsigned_t<_Ty1>>(_Left) == _Right && _Left >= 0;
  }
}

VPSUBUSB	z, x, y

VPMINUB		z, x, y
VPCMPEQB	w, z, x

我不太懂。不评价

用avx处理字符串中的斜杠,比如my title is \"La vie\"

通常写法

  for (...) {
    if ((*in == '\\') || (*in == '"')) {
      *out++ = '\\';
    }
    *out++ = *in;
  }

sse/avx写法

 __m512i solidus = _mm512_set1_epi8('\\');
  __m512i quote = _mm512_set1_epi8('"');
  for (; in + 32 <= finalin; in += 32) {
    __m256i input = _mm256_loadu_si256(in);
    __m512i input1 = _mm512_cvtepu8_epi16(input);
    __mmask64 is_solidus = _mm512_cmpeq_epi8_mask(input1, solidus);
    __mmask64 is_quote = _mm512_cmpeq_epi8_mask(input1, quote);
    __mmask64 is_quote_or_solidus = _kor_mask64(is_solidus, is_quote);
    __mmask64 to_keep = _kor_mask64(is_quote_or_solidus, 0xaaaaaaaaaaaaaaaa);
    __m512i shifted_input1 = _mm512_bslli_epi128(input1, 1);
    __m512i escaped =
        _mm512_mask_blend_epi8(is_quote_or_solidus, shifted_input1, solidus);
    _mm512_mask_compressstoreu_epi8(out, to_keep, escaped);
    out += _mm_popcnt_u64(_cvtmask64_u64(to_keep));
  }

给我看困了

这里有个类似的https://branchfree.org/2019/03/06/code-fragment-finding-quote-pairs-with-carry-less-multiply-pclmulqdq/

这段代码有bug,不懂这几个API的可能看不懂

bool ShuttingDown = false;

void MainThread()
{
    DWORD id;
    auto hThread = CreateThread(nullptr, 0, WorkerThread,
                                nullptr, 0, &id); // succeeds

    BlahBlahBlah(); // do useful work

    // Time to clean up. Post an APC to the worker thread
    // to tell it that it's time to go home.
    QueueUserAPC(WakeWorker, hThread, 0); // succeeds

    WaitForSingleObject(hThread, INFINITE); // hangs

    CloseHandle(hThread);
}

void CALLBACK WakeWorker(ULONG_PTR)
{
    ShuttingDown = true;
}

DWORD CALLBACK WorkerThread(void*)
{
    // Do work until shut down.
    do
    {
        // All work is posted via APCs.
        SleepEx(INFINITE, TRUE);
    } while (!ShuttingDown);

    return 0;
}

简单来说SleepEx通过QueueUserAPC来唤醒,有一种场景,QueueUserAPC唤醒了,但是CreateThread执行的慢,导致SleepEx没收到通知,从而永远死锁

怎么解决这个问题?把do while循环改成while就行了。因为这种场景已经shutdown了,不应该执行sleep

DWORD CALLBACK WorkerThread(void*)
{
    // Do work until shut down.
    while (!ShuttingDown)
    {
        // All work is posted via APCs.
        SleepEx(INFINITE, TRUE);
    } 

    return 0;
}

讲协程的。说来惭愧我还不是很懂。就不介绍了

视频

简单来说只有clang编译器支持std::format

开源项目需要人手

新项目介绍/版本更新

工作招聘

寒冬了。保佑我不被开

本文永久链接

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