C++ 中文周刊 第126期

2024-07-30 14:19:59 浏览数 (3)

周刊项目地址[1]

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

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

请提交 issue[2]

感谢不语赞助

资讯

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

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

boost::async review https://github.com/klemens-morgenstern/async

https://www.reddit.com/r/cpp/comments/15l8xju/review_of_proposed_boostasync_begins/

比较集成在ASIO上

文章

cppwinrt is in maintenance mode[3]

感觉cppwinrt还没开发多久,就维护状态了,又引入了新库 https://github.com/microsoft/wil/

说实话,微软总搞这种事,开发一个新技术,让大伙学,还没怎么学明白,换坑,再重新学,和google有一拼

之前更新了很多winrt的文章,得亏一个没看,他妈的,纯纯浪费生命,微软我___

Did you know that C 26 added 'A nice placeholder with no name' [4]
代码语言:javascript复制
auto foo() { return 42; }

int main() {
    auto unused = foo(); // warning in C  23
    auto _ = foo();      // no warning in C  26
}

话说,我以前是这么玩的, std::ignore[5]

代码语言:javascript复制
decltype(std::ignore) _;
_ = blahblah();
Inside STL: The lists[6]

介绍链表实现,MSVC和gcc/clang的实现还不太一样, 单向链表都差不多

代码语言:javascript复制
template<typename T>
struct forward_list {
    forward_list_node<T>* head;
};

template<typename T>
struct forward_list_node {
    forward_list_node<T>* next;
    T value;
};

双向链表,有个边界判定问题

代码语言:javascript复制
template<typename T>
struct list {
    list_node_base<T> head; // or "list_node_base<T>* head;"
    size_t size;
};

template<typename T>
struct list_node_base {
    list_node<T>* next;
    list_node<T>* prev;
};

template<typename T>
struct list_node : list_node_base<T> {
    T value;
};

可以有个dummy node来管理开头结尾,也可不用,多一个内存分配,这也是msvc需要注意的地方,list构造可能抛异常

Inside STL: The map, set, multimap, and multiset[7]

基本结构就这样

代码语言:javascript复制
struct tree {
    node_base header; // or "node_base* header;"
    size_t size;
};

struct node_base {
    node_base* parent;
    node_base* left;
    node_base* right;
};

struct node : node_base {
    bool color; // red or black
    payload data;
};
Five Advanced Initialization Techniques in C : From reserve() to piecewise_construct and More.[8]

介绍几种高效的用法,比如vector reserve emplace_back,这种大家都知道,代码就不贴了

lambda 捕获优化

代码语言:javascript复制
auto result = std::find_if(vs.begin(), vs.end(),
        [&prefix](const std::string& s) {
            return s == prefix   "bar"s; 
        }
    );

这种使用捕获用法,每次都要生成对象,效率低下,需要改成

代码语言:javascript复制
result = std::find_if(vs.begin(), vs.end(), 
        [savedString = prefix   "bar"s](const std::string& s) { 
            return s == savedString; 
        }
    );

这样,避免每次lambda都构造

unique_ptr优化 make_unique_for_overwrite,有时候你想用unique_ptr管理 buffer,但没有必要清零,可以用这个接口,类似stringresize_for_overwrite

代码语言:javascript复制
auto ptr = std::make_unique_for_overwrite<int[]>(1000);

pair/tuple优化 piecewise_construct forward_as_tuple

代码语言:javascript复制
// 1
std::cout << "regular: n";
std::pair<MyType, MyType> p { MyType{"one", 1}, MyType{"two", 2}};

// 2
std::cout << "piecewise   forward: n";
std::pair<MyType, MyType>p2(std::piecewise_construct,
            std::forward_as_tuple("one", 1),
            std::forward_as_tuple("two", 2));

起到一个就地构造的效果,类似emplace_back

对于map来说,这种也能用

代码语言:javascript复制
#include <string>
#include <map>

struct Key {
    Key(int a, int b) : sum(a   b) {}
    int sum;
    bool operator<(const Key& other) const { 
        return sum < other.sum; 
    }
};

struct Value {
    Value(const std::string& s, double d) : name(s), data(d) {}
    std::string name;
    double data;
};

int main() {
    std::map<Key, Value> myMap;

    // doesn't compile: ambiguous
    // myMap.emplace(3, 4, "example", 42.0);

    // works:
    myMap.emplace(
        std::piecewise_construct,
        std::forward_as_tuple(3, 4),  
        std::forward_as_tuple("example", 42.0) 
    );
}
Fun with quadratic pack-expansions[9]
代码语言:javascript复制
consteval bool all_of(const auto& f, const auto&... xs) {
  return (f(xs) && ...);
}

consteval bool contains(const auto& n, const auto&... hs) {
  return ((hs == n) || ...);
}

consteval int count(const auto& n, const auto&... hs) {
  return (0   ...   (hs == n));
}

static_assert(count(1, 3,1,4,1,6) == 2);
static_assert(count(2, 3,1,4,1,6) == 0);

这都很常规

类似的,可以写个判断重复

代码语言:javascript复制
consteval bool has_any_duplicates() { return false; }
consteval bool has_any_duplicates(const auto& n, const auto&... hs) {
  return ((n == hs) || ...) || has_any_duplicates(hs...);
}

static_assert(has_any_duplicate(3,1,4,1,6));
static_assert(!has_any_duplicate(2,7,1,8,3));
static_assert(has_any_duplicate(9,9,9));
static_assert(!has_any_duplicate(9));
static_assert(!has_any_duplicate());
代码语言:javascript复制

有点递归了,感觉和count有点像?用count改写

代码语言:javascript复制
constexpr auto has_duplicate_of(const auto& value) {
  return [&](const auto&... hs) {
    return count(value, hs...) >= 2;
  };
}
consteval bool has_any_duplicates(const auto&... hs) {
  return (has_duplicate_of(hs)(hs...) || ...);
}

再简化一下,不用 has_duplicate_of

代码语言:javascript复制
consteval bool has_any_duplicates(const auto&... hs) {
  return ((count(hs, hs...) >= 2) || ...);
}

不用count再简化一下

代码语言:javascript复制
consteval bool has_any_duplicates(const auto&... hs) {
  return ([&](const auto& n) { return (0   ...   (hs == n)) >= 2; }(hs) || ...);
}

还有优化空间! 参数n可以优化

代码语言:javascript复制
consteval bool has_any_duplicates(const auto&... hs) {
  return ([&,&n=hs]{ return (0   ...   (hs == n)) >= 2; }() || ...);
}
A case in optimizing auto-vectorized code[10]

手把手教你写SIMD代码。看晕了 代码在这里https://github.com/oliora/habr-switches-perf-test

Inside STL: The unordered_map, unordered_set, unordered_multimap, and unordered_multiset[11]

hashtable的内存结构,基本就这样

代码语言:javascript复制
struct hashtable{
    using hint = std::list<payload>::iterator;

    std::list<payload> list;
    std::vector<hint> buckets;
};

开链在现在的硬件看来已经证明是缓存不友好的了,flatmap可以解决这一点

Inside STL: The deque, design[12]

可以这样理解

代码语言:javascript复制
template<typename T>
struct simple_deque {
    T* elements;
    T* first;
    T* last;
    size_t capacity;
};

/*
                           first    last
elements | ? |  ? | ? | ? | 1 | 2 | 3 | ? |

size = last - first  =3

cap = 8
*/

大概就是这样的结构 从中间往外展开,如果空间用光,就分配alloc一条新的数组,然后管理数组间的串联关系

Inside STL: The deque, implementation[13]

列举各种实现的差异

On the optimal bounds for integer division by constants[14]

看晕了

C 20 Dynamic Allocations at Compile-time[15]

老文章,看代码

代码语言:javascript复制
consteval auto as_constant(auto value) {
    return value;
}
consteval int Calc(int x) {  return 4 * x; }
int main() {
    auto res = Calc(2); 
    // auto res = as_constant(Calc(2)); 
      res;  
    res = Calc(res); //编译不过
    return res;
  }

如果改成as_constant就能编译过。as_constant看起来傻逼,但是强制编译期计算了

Making your own array[16]

造轮子,没觉得有啥意思 代码这里 https://github.com/PipeRift/pipe/blob/feature/custom-arrays/Include/Pipe/PipeArrays.h

C 23: multidimensional operator[][17]

看代码, 多维数组访问下标

代码语言:javascript复制
#include <vector>
#include <https://raw.githubusercontent.com/kokkos/mdspan/single-header/mdspan.hpp>
#include <iostream>

int main()
{
  std::vector v = {1,2,3,4,5,6,7,8,9,10,11,12};

  // View data as contiguous memory representing 2 rows of 6 ints each
  auto multispan = std::experimental::mdspan(v.data(), 2, 6);
  std::cout << multispan[0, 1] << 'n'; // 2
}
Passkey Idiom: A Useful Empty Class[18]

想让对象通过特别的factory来构造。自己不能构造

看代码。没啥说的,就是private tag类限制

tag类甚至可以不用

代码语言:javascript复制
class Secret {
  class ConstructorKey {
    friend class SecretFactory;
  private:
    ConstructorKey() {};
    ConstructorKey(ConstructorKey const&) 
      = default;
  };
public:
  //Whoever can provide a key has access:
  explicit Secret(std::string str,
  ConstructorKey) : data(std::move(str)) {}
private:
  //these stay private, since Secret itself has
  // no friends any more
  void addData(std::string const& moreData);
  std::string data;
};
class SecretFactory {
public:
  Secret getSecret(std::string str) {
    return Secret{std::move(str), {}}; 
  }
  // void modify(Secret& secret, 
  // std::string const& additionalData) {
  //   secret.addData(additionalData);   //ERROR:
  //       // void Secret::addData(const string&)
  //                                // is private
  // }
};
int main() {
  Secret s{"foo?", {}};    //ERROR:
  // Secret::ConstructorKey::ConstructorKey()
  // is private
  SecretFactory sf;
  Secret s = sf.getSecret("moo!"); //OK
}
The downsides of C Coroutines[19]

无栈协程各种缺点

  • • 参数生命周期问题
代码语言:javascript复制
task<void> async_insert(T && val);

task<void> async_find(const T &);

task<void> async_write(span<byte>);

这种不注意生命周期一不留神就用错

  • • 迭代器安全
代码语言:javascript复制
task<void> send_all(string s) {
  for (auto & source : m_sources)
  {
    co_await source.send(s);
  }
}

看上去没问题,如果m_sources有改动,就完了

所以复制一下是不是就没问题了?

代码语言:javascript复制
task<void> send_all(string s)
{
  std::vector<task<void>> sends;
  sends.reserve(m_sources.size());
  for (auto & source : m_sources)
  {
    sends.push_back(source->send(s));
  }

  co_await wait_all( sends.begin(), sends.end() );
}

等一下,m_sources如果析构了呢?

保活一下

代码语言:javascript复制
task<void> send_all(string s)
{
  std::vector<task<void>> sends;
  std::vector<shared_ptr<Source>> sources = m_sources;
  sends.reserve(sources.size());
  for (auto & source : sources)
  {
    sends.push_back(source->send(s));
  }

  co_await wait_all( sends.begin(), sends.end() );
}

或者有个爹

代码语言:javascript复制
struct Source
{
  std::vector<std::coroutine_handle> dependent_coroutines;
  ~Source()
  {
    for (auto & coroutine : dependent_coroutines)
      coro.destroy();
  }

  task<void> send(string s)
  {
    auto corohandle = co_await get_current_coroutine{};
    dependent_coroutines.push_back( corohandle );
    ...
    dependent_coroutines.erase( dependent_coroutines.find( corohandle ) );
  }
}

或者加个锁?

代码语言:javascript复制
task<void> send_all(string s)
{
  co_await m_sourcesLock.read_lock();
  std::vector<task<void>> sends;
  sends.reserve(m_sources.size());
  for (auto & source : m_sources)
  {
    sends.push_back(source->send(s));
  }

  co_await wait_all( sends.begin(), sends.end() );
  co_await m_sourcesLock.read_unlock();
}

已经想吐了

  • • 协程执行的可快可慢
代码语言:javascript复制
co_await fetch_data("key");

这代码的问题相信你能看出来 key的生命周期是临时的。如果立即执行这个协程,不会崩,你显然没发现这个bug

然后这种代码越来越多,突然某一天就崩了,你还在纳闷咋回事

哦你发现了,想了想,保存一下,延长一下生命周期,应该行了吧

代码语言:javascript复制
eager_task<string> fetch_data(string_view key)
{
  auto it = cache.find(key)
  if (it != cache.end())
  {
    return it->second;
  }

  auto data = co_await fetch_remote(key);
  cache.emplace(key, data);
}

但是指不定某个兄弟就这么写了

代码语言:javascript复制
eager_task<void> fetch_mydata(string_view key)
{
  return fetch_data(std::format("mysystem/{}", key));
}

同样的崩溃问题又出现了

  • • 测试问题,怎么避免上面这种用法的出现?await_transform?
  • • 性能问题,内存分配的浪费?即使有HALO优化,还是会有很大的栈空间浪费?
  • • 还是性能问题,生成的函数太多了,debug build很多浪费,这个无解,只能等编译器优化

视频

  • • A Graph Based Update System in C : Lightning Updates - Hana Dusíková [20]

讲的玩意类似taskflow,但是是用hash来串联类型依赖。没有源代码

  • • Function Contracts in Practice using C - Rostislav Khlebnikov - ACCU 2023[21]

讲函数怎么写assert写约束写断言,还算有点意思,周末整理一下这个

开源项目需要人手

  • • asteria[22] 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群384042845和作者对线
  • • Unilang[23] deepin的一个通用编程语言,点子有点意思,也缺人,感兴趣的可以github讨论区或者deepin论坛看一看。这里也挂着长期推荐了
  • • gcc-mcf[24] 懂的都懂

新项目介绍/版本更新

  • • https://github.com/SpartanJ/efsw filewatch 基于inotify
  • • https://github.com/mpusz/mp-coro 一个协程库
  • • https://github.com/cppalliance/mrdox 一个文档生成工具

工作招聘

有没有数据库相关的工作推荐我一下,我要失业了快

本文永久链接[25]

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

引用链接

[1] 周刊项目地址: https://github.com/wanghenshui/cppweeklynews [2] 提交 issue: https://github.com/wanghenshui/cppweeklynews/issues [3] cppwinrt is in maintenance mode: https://github.com/microsoft/cppwinrt/issues/1289#issuecomment-1481303844 [4] Did you know that C 26 added 'A nice placeholder with no name' : https://github.com/tip-of-the-week/cpp/blob/master/tips/342.md [5] std::ignore: https://en.cppreference.com/w/cpp/utility/tuple/ignore [6] Inside STL: The lists: https://devblogs.microsoft.com/oldnewthing/20230804-00/?p=108547 [7] Inside STL: The map, set, multimap, and multiset: https://devblogs.microsoft.com/oldnewthing/20230807-00/?p=108562 [8] Five Advanced Initialization Techniques in C : From reserve() to piecewise_construct and More.: https://www.cppstories.com/2023/five-adv-init-techniques-cpp/ [9] Fun with quadratic pack-expansions: https://quuxplusone.github.io/blog/2023/08/05/quadratic-pack-expansions/ [10] A case in optimizing auto-vectorized code: https://oliora.github.io/2023/08/07/Optimizing-auto-vectorized-code.html [11] Inside STL: The unordered_map, unordered_set, unordered_multimap, and unordered_multiset: https://devblogs.microsoft.com/oldnewthing/20230808-00/?p=108572 [12] Inside STL: The deque, design: https://devblogs.microsoft.com/oldnewthing/20230809-00/?p=108577 [13] Inside STL: The deque, implementation: https://devblogs.microsoft.com/oldnewthing/20230810-00/?p=108587 [14] On the optimal bounds for integer division by constants: https://jk-jeon.github.io/posts/2023/08/optimal-bounds-integer-division/ [15] C 20 Dynamic Allocations at Compile-time: https://accu.org/journals/overload/31/176/fertig/ [16] Making your own array: https://muit.xyz/posts/making-your-own-array/ [17] C 23: multidimensional operator[]: https://www.sandordargo.com/blog/2023/08/09/cpp23-multidimensional-subscription-operator [18] Passkey Idiom: A Useful Empty Class: https://accu.org/journals/overload/31/176/mertz/ [19] The downsides of C Coroutines: https://reductor.dev/cpp/2023/08/10/the-downsides-of-coroutines.html [20] A Graph Based Update System in C : Lightning Updates - Hana Dusíková : https://www.youtube.com/watch?v=C9MWAXYdFSY&ab_channel=CppNow [21] Function Contracts in Practice using C - Rostislav Khlebnikov - ACCU 2023: https://www.youtube.com/watch?v=5ttyA1-I8D8&ab_channel=ACCUConference [22] asteria: https://github.com/lhmouse/asteria [23] Unilang: https://github.com/linuxdeepin/unilang [24] gcc-mcf: https://gcc-mcf.lhmouse.com/ [25] 本文永久链接: https://wanghenshui.github.io/cppweeklynews/posts/126.html

0 人点赞