C++ 中文周刊 第142期

2024-07-30 14:46:45 浏览数 (1)

周刊项目地址 https://github.com/wanghenshui/cppweeklynews

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

欢迎投稿,推荐或自荐文章/软件/资源等,评论私信都可以

文章大部分来自

https://discu.eu/weekly/candcpp/2023/49/

https://www.meetingcpp.com/blog/blogroll/items/Meeting-Cpp-weekly-Blogroll-408.html

本期文章由 黄亮Anthony 赞助

资讯

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

编译器信息最新动态推荐关注hellogcc公众号 OSDT Weekly 2023-12-13 第232期

另外PLCT有rsicv竞赛,感兴趣的可以参加一下 rvspoc.org

boost发布1.84版本,c 03全部抛弃,windows只支持win10及以上

新增redis cobalt库之前讲过

另外asio移除了execution相关设计。。

PFR支持fieldname 反射,要求c 20 https://github.com/boostorg/pfr/pull/129/files

效果 https://godbolt.org/z/xbo7bos86

代码语言:javascript复制
#include <https://raw.githubusercontent.com/denzor200/pfr/amalgamate_get_name/include/boost/pfr/gen/pfr.hpp>
#include <functional>
#include <cstdio>
#include <cstring>

struct Any {
    Any() {};
};

struct XClass {
    int member1;
    Any this_is_a_name; // not constexpr constructible
    std::reference_wrapper<char> c; // not default constructible
};

int main() {
    char buf[32] {0};
    constexpr auto first = boost::pfr::get_name<0, XClass>();
    memcpy(buf, first.data(), first.size());
    puts(buf);
    
    static_assert("member1"        == boost::pfr::get_name<0, XClass>());
    static_assert("this_is_a_name" == boost::pfr::get_name<1, XClass>());
    static_assert("c"              == boost::pfr::get_name<2, XClass>());
}

Unordered支持concurrent_flat_set以及并发visit

其他的没啥说的。自己看吧

https://www.boost.org/users/history/version_1_84_0.html

文章

  • • Text Editor Data Structures https://cdacamar.github.io/data structures/algorithms/benchmarking/text editors/c /editor-data-structures/
  • • Text Editor Data Structures: Rethinking Undo https://cdacamar.github.io/data structures/algorithms/benchmarking/text editors/c /rethinking-undo/

代码在这里 https://github.com/cdacamar/fredbuf

手把手教你实现编辑器

  • • Measuring the size of the cache line empirically https://lemire.me/blog/2023/12/12/measuring-the-size-of-the-cache-line-empirically/

在130期咱们就聊过,如果cacheline 64,设置align 128能降低影响。lemire给了一种简单的测试方法,拷贝数组

https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2023/12/12/cacheline.c

小于cacheline,带宽没啥区别,受cacheline影响了,大于cacheline,越大越快,理论上加入cacheline 64 拷贝128,应该获得翻倍的速度,但是实际上并不是

建议大家自己玩玩,测一下效果。在m1表现也不太一样,但是小于cacheline拷贝速度不变这个现象是不变的

  • • A Bug in the Priority Scheduler for Coroutines https://www.modernescpp.com/index.php/a-bug-in-the-priority-scheduler-for-coroutines/

140期的调度器实现有bug,问题代码

代码语言:javascript复制
class Scheduler {

  std::priority_queue<job, std::vector<job>, Comperator> _prioTasks;

  public: 
    void emplace(int prio, std::coroutine_handle<> task) {
      _prioTasks.push(std::make_pair(prio, task));
    }
};
Task createTask(const std::string& name) {
  std::cout << name << " startn";
  co_await std::suspend_always();
  for (int i = 0; i <= 3;   i ) { 
    std::cout << name << " execute " << i << "n";                  // (5)
    co_await std::suspend_always();
  }
  co_await std::suspend_always();
  std::cout << name << " finishn";
}

scheduler1.emplace(0, createTask("TaskA").get_handle());

看出来哪里有问题没有?createtask 的name的生命周期问题

  • • In C , how can I make a member function default parameter depend on this? https://devblogs.microsoft.com/oldnewthing/20231206-00/?p=109108

成员函数参数不能直接用this

代码语言:javascript复制
struct Sample
{
    int increment;
    void add(int v = increment); // not allowed
    void notify_all(Sample* source = this); // not allowed
};

猥琐绕过

代码语言:javascript复制
struct Sample
{
    int increment;

    void add(int v);
    void add() { add(increment); }

    void notify_all(Sample* source);
    void notify_all() { notify_all(this); }
};

Sample s;

s.add(2); // adds 2
s.add(); // adds s.increment

s.notify_all(); // uses source = s
s.notify_all(other); // uses source = other
  • • Higher quality random floats https://www.corsix.org/content/higher-quality-random-floats

随机浮点数

比如 这个实现 https://dotat.at/@/2023-06-23-random-double.html

代码语言:javascript复制
double pcg64_random_double(pcg64_t *rng) {
    return (double)(pcg64_random(rng) >> 11) * 0x1.0p-53;
}

luajit是这样的

代码语言:javascript复制
uint64_t lj_prng_u64d(PRNGState *rs) {
    uint64_t z, r = 0;
    TW223_STEP(rs, z, r)
    /* Returns a double bit pattern in the range 1.0 <= d < 2.0. */
    return (r & 0x000fffffffffffffull) | 0x3ff0000000000000ull;
}
/* Then to give d in [0, 1) range: */
U64double u;
double d;
u.u64 = lj_prng_u64d(rs);
d = u.d - 1.0;

lemire博士的golang版本

代码语言:javascript复制
// toFloat64 -> [0,1)
func toFloat64(seed *uint64) float64 {
    x := splitmix64(seed)
    x &= 0x1fffffffffffff // %2**53
    return float64(x) / float64(0x1fffffffffffff)
}

原理是这个 https://www.zhihu.com/question/25037345/answer/29879012

01之间

代码语言:javascript复制
double rand_between_zero_and_one() {
    double d;
    uint64_t x = rand_u64() >> 11; /* 53-bit uniform integer */
    uint64_t e = 1022;
    do {
      if (rand_u64() & 1) break; /* 1-bit uniform integer */
      e -= 1;
    } while (e > 1022-75);
    x = ((x   1) >> 1)   (e << 52);
    memcpy(&d, &x, sizeof(d));
    return d;
}

优化

代码语言:javascript复制
double rand_between_zero_and_one() {
    double d;
    uint64_t x = rand_u64();
    uint64_t e = __builtin_ctzll(x) - 11ull;
    if ((int64_t)e >= 0) e = __builtin_ctzll(rand_u64());
    x = (((x >> 11)   1) >> 1) - ((e - 1011ull) << 52);
    memcpy(&d, &x, sizeof(d));
    return d;
}

主要是要懂浮点数格式以及如何恰当的均匀分布

  • • Six Handy Operations for String Processing in C 20/23 https://www.cppstories.com/2023/six-handy-ops-for-string-processing/

contains

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

int main(){
    const std::string url = "https://isocpp.org";
    
    if (url.contains("https") && 
        url.contains(".org") && 
        url.contains("isocpp"))
        std::cout << "you're using the correct site!n";
}

starts_with(), ends_with()

insert range

代码语言:javascript复制
#include <iostream>
#include <iterator>
#include <string>
 
int main() {
    const auto source = {'l', 'i', 'b', '_'};
    std::string target{"__cpp_containers_ranges"};
 
    const auto pos = target.find("container");
    auto iter = std::next(target.begin(), pos);
 
#ifdef __cpp_lib_containers_ranges
    target.insert_range(iter, source);
#else
    target.insert(iter, source.begin(), source.end());
#endif
 
    std::cout << target;
}

spanstream

代码语言:javascript复制
#include <iostream>
#include <sstream>
#include <spanstream> // << new headeer!

void* operator new(std::size_t sz){
    std::cout << "Allocating: " << sz << 'n';
    return std::malloc(sz);
}

int main() {
    std::cout << "start...n";
    std::stringstream ss;
    ss << "one string that doesn't fit into SSO";
    ss << "another string that hopefully won't fit";

    std::cout << "spanstream:n";
    char buffer[128] { 0 };
    std::span<char> spanBuffer(buffer);
    std::basic_spanstream<char> ss2(spanBuffer);
    ss2 << "one string that doesn't fit into SSO";
    ss2 << "another string that hopefully won't fit";

    std::cout << buffer;
}
  • • How Many Lines of C it Takes to Execute a b in Python? https://codeconfessions.substack.com/p/cpython-dynamic-dispatch-internals

想了解cpython的可以看看

  • • C 23: The rise of new streams https://www.sandordargo.com/blog/2023/12/06/cpp23-strtream-strstream-replacement

介绍spanstream的, 直接贴代码了

代码语言:javascript复制
#include <iostream>
#include <span>
#include <spanstream>
#include <cassert>

void printSpan(auto spanToPrint) {
    for (size_t i = 0; i < spanToPrint.size();   i) {
        std::cout << spanToPrint[i];
    }
}

void useSpanbuf() {
    std::array<char, 16> charArray;
    std::span<char, 16> charArraySpan(charArray);
    std::spanbuf buf;

    char c = 'a';
    for (size_t i = 0; i < 16;   i) {
        charArraySpan[i] = c;
          c;
    }
    
    buf.span(charArraySpan);

    // we can easily print a span got from the buffer
    std::span bufview = buf.span();
    std::cout << "bufview: ";
    for (size_t i = 0; i < 16;   i) {
        std::cout << bufview[i];
    }
    std::cout << 'n';
}

void useSpanstream() {
    std::array<char, 16> charArray;
    std::ospanstream oss(charArray);

    oss << "Fortytwo is " << 42;
    // copying the contents to a span
    std::string s{oss.span().data(),size_t(oss.span().size())};
    assert(s == "Fortytwo is 42");
}


int main() {
    useSpanbuf();
    useSpanstream();

    return 0;
}

Raymond chen环节。我直接贴连接了。window是我不懂

  • • How can I work around the absence of default parameters in the Windows Runtime? https://devblogs.microsoft.com/oldnewthing/20231213-00/?p=109144
  • • How do I specify an optional parameter to a Windows Runtime method? https://devblogs.microsoft.com/oldnewthing/20231214-00/?p=109146

linux环节

  • • 消失的内存之共享内存shmem https://zhuanlan.zhihu.com/p/666268134

shmem tmpfs 比较经典

  • • 使用pidfd实现Linux跨进程传递文件描述符 https://zhuanlan.zhihu.com/p/672314758

linux 5.6引入的,有意思

视频

  • • C Horizons - Bryce Adelstein Lelbach - Meeting C 2023 https://www.youtube.com/watch?v=og4_Tm-Hkjw

姚奕正qds推荐

把 reflection/injection, pattern matching, senders都说了一遍,可以算是一个完全的新c

  • • C Weekly - Ep 406 - Why Avoid Pointer Arithmetic? https://www.youtube.com/watch?v=MsujPM2wDmk

在指针上做计算风险高,这也是为啥要引入span stringview,不用char * 信息丢失太多

cpponsea 2023

  • • Lightning Talk: I Need a Different Variant in C - Robert Allan Hennigan Leahy https://www.youtube.com/watch?v=Wgpm79yRLoI

考虑一个场景,

代码语言:javascript复制
std::variant<int, float> foo();

std::variant<int, float,double> bar(){
  auto v = foo();
  ...
}

显然涉及到从std::variant不同参数的转换,怎么写?

代码语言:javascript复制
template<class From, class To>
constexpr To variant_cast(From&& from) {
  return std::visit([](auto&& a) constexpr noexcept(
      std::is_nothrow_constructible_v<
        To,
        std::in_place_type_t<std::remove_cvref_t<decltype(a)>>,
        decltype(a)
        >) -> To {
          return To(
            std::in_place_type<std::remove_cvref_t<decltype(a)>>,
            std::forward<decltype(a)>(a));
      },
      std::foward<From>(from)
    );
}

存在一个问题,就是从std::variant<int, float>std::variant<int, double> 没法转,太safe了,得放宽一点

代码语言:javascript复制
template<class To>
struct visitor {
  template<class T>
  requires std::constructible_from<To,std::in_place_type_t<std::remove_cvref_t<T>>,T>
  constexpr To operator()(T&& t) const noexcept(
    std::is_nothrow_constructible_v<To,std::in_place_type_t<std::remove_cvref_t<T>>,T>){
        return To(
          std::in_place_type<std::remove_cvref_t<decltype(a)>>,
          std::forward<T>(t));
  }
  template<class T>
  constexpr To operator()(T&& t) const noexcept {
    std::unreachable();
  }

};

template<class To, class From>
constexpr To uncheck_variant_cast(From&& from) {
  return std::visit(visitor<To>{}, std::forward<From>(from));
}

其实就是舍弃内部值本来的类型,用From硬推,匹配不到就失败

问题来了,如果是std::variant<const int, float>怎么办?

别琢磨了,能做,不支持,去他妈的,你就非得这么写吗,No

有意思的项目

  • • asteria https://github.com/lhmouse/asteria 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群75330236
  • • Unilang https://github.com/linuxdeepin/unilang deepin的一个通用编程语言,点子有点意思,也缺人,感兴趣的可以github讨论区或者deepin论坛看一看。这里也挂着长期推荐了
  • • https://github.com/couchbase/fleece 一种json实现,说是性能很好,但没有和bson cbor的横向对比。有空测一下

互动环节

上一期提到的有问题的代码

代码语言:javascript复制
int median(std::vector<int>& v) {
   int mid = v.size() / 2;
   std::nth_element(v.begin(), v.begin()   mid, v.end());
   int result = v[mid];
   if (v.size() % 2 == 0) {
     std::nth_element(v.begin(), v.begin()   mid - 1, v.end());
     result = (v[mid]   v[mid-1])/2;  
     // result = (result   v[mid-1]) /2;
   }
   return result;
}

周星星指出,完全可以第二个std::nth_element改成 std::max_element(begin,begin mid)

确实是一个思路,或者更极端一点,偶数也不特殊处理就好了

huring指出这个问题在macbook m1上不复现我的提供的 10,10,29,18,10,10,10,10,13,32无法复现

只能说和平台也有点关系,巧合而已

突击提问:如何实现nth_element?最近面试刚被问到,我没上来优化的部分。已经自罚十个深蹲了

0 人点赞