C++ 中文周刊 第99期

周刊项目地址

公众号

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

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

提交 issue

20230203


资讯

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

编译器信息最新动态推荐关注hellogcc公众号 2023-02-01 第187期

有武汉线下活动,可以关注

fishshell这个项目宣布用rust重写了。理由是c++太差了周边编译设施等等太难用了之类的

对此笔者锐评:确实

reddit社区对此锐评: Stop Comparing Rust to Old C++

我觉得有点露怯了,实话实说,构建确实不好用,演进也慢,你说meson conan能用我只能说还差点意思

而且c++开源社区开发人员也差点意思,没那个功夫,但rust就很不一样,很多人愿意拿rust练手。

只能说c++周边的演进还需要加快一些。周边文章咨询写的多一些,更通俗易懂一些,知道的人越多越好。大家多写文章多分享啊

本周还有一个事情是yandex的代码泄露,磁力链接在这个答案可以看到,https://www.zhihu.com/question/580980335/answer/2867507106

这两天看了下,cpp的项目挺有意思,但两个组件库util和mapreduce是没泄露的,比较可惜,要是都泄露了我就要创业了

不过一个成熟的引擎,数据是最重要的,没有数据,工具没啥意义。

他们的代码接口风格还是98那套Interface形式,但是都用的pragram once,没用Macro Guard,给人一种老太太用神仙水的反差感

另外有人问为啥他们数据库代码没泄露。。他们用的ydb本身就是开源的。

文章

jetbrains出的报告,关于c++20使用,编译工具cmake使用率,代码分析使用率做了个列举,这里直接贴一下

构建工具,cmake越来越普及,虽然难用

什么?你想问包管理工具?

嘻嘻,没人用

用什么测试框架?

甚至不写测试

使用什么代码分析工具?

嘻嘻,甚至不用分析工具

完整报告可以去他们官网看。这里不列举了

其实就是链式构造啦

template<typename This>
struct ToggleBuilder {
    template<typename... T>
    auto& When(bool flag, auto f, T&&... params)
    {
        auto& actualThis = static_cast<This&>(*this);
        if (flag)
        {
            std::invoke(f, actualThis, std::forward<T>(params)...);
        }
        return actualThis;
    }
};
 
class MessageBuilder : public ToggleBuilder<MessageBuilder> {
   MessageBuilder& WithPayload(std::string payload)
    {
        m_payload = std::move(payload);
        return *this;
    }
     
    //...
};

Message CreateMessage(std::string payload, const Config& config)
{
    return MessageBuilder{}
        WithPayload(std::move(payload)).        
        When(config.someSpecialRecipeEnabled, [](MessageBuilder& builder){ 
            return builder.WithAppender(GetSpecialAppenderParams()).
                           WithAppender(GetAnotherSpecialAppenderParams());
        }).
        // ...
        .Build();
}

来个现实生活中的例子,OnnxRuntime

auto session = SessionBuilder{}.
    WithLogger(MakeLoggerFrom(config)).
    WithModel(config.modelPath).
    WithCUDAExecutionProvider(config.cudaOptions).
    WithOpenVINOExecutionProvider(config.openVinoOptions).
    WithOptimizationLevel(config.optimizationLevel).
    WithNumberOfIntraThreads(config.intraThreads).
    //...
    Build();

用上面那个设计,相当于

auto session = SessionBuilder{}.
    WithLogger(MakeLoggerFrom(config)).
    WithModel(config.modelPath).
    When(config.useCuda, &SessionBuilder::WithCUDAExecutionProvider, config.cudaOptions).
    When(config.useOpenVino, &SessionBuilder::WithOpenVINOExecutionProvider, config.openVinoOptions).
    WithOptimizationLevel(config.optimizationLevel).
    WithNumberOfIntraThreads(config.intraThreads).
    //...
    Build();

当然 rust这种代码也有很多

显然能想到

std::string output = std::to_string(address >> 24);
for (int i = 2; i >= 0; i--) {
  output.append(std::to_string((address >> (i * 8)) % 256) + ".");
}

使用to_chars

std::string output(4 * 3 + 3, '\0'); // allocate just one big string
char *point = output.data();
char *point_end = output.data() + output.size();
point = std::to_chars(point, point_end, uint8_t(address >> 24)).ptr;
for (int i = 2; i >= 0; i--) {
 *point++ = '.';
 point = std::to_chars(point, point_end, uint8_t(address >> (i * 8))).ptr;
}
output.resize(point - output.data());

干脆查表

char *to_chars_52(char *p, unsigned char x) {
  constexpr std::string_view table[256] = {
      "0",   "1",   "2",   "3",   "4",   "5",   "6",   "7",   "8",   "9",
      "10",  "11",  "12",  "13",  "14",  "15",  "16",  "17",  "18",  "19",
      "20",  "21",  "22",  "23",  "24",  "25",  "26",  "27",  "28",  "29",
      "30",  "31",  "32",  "33",  "34",  "35",  "36",  "37",  "38",  "39",
      "40",  "41",  "42",  "43",  "44",  "45",  "46",  "47",  "48",  "49",
      "50",  "51",  "52",  "53",  "54",  "55",  "56",  "57",  "58",  "59",
      "60",  "61",  "62",  "63",  "64",  "65",  "66",  "67",  "68",  "69",
      "70",  "71",  "72",  "73",  "74",  "75",  "76",  "77",  "78",  "79",
      "80",  "81",  "82",  "83",  "84",  "85",  "86",  "87",  "88",  "89",
      "90",  "91",  "92",  "93",  "94",  "95",  "96",  "97",  "98",  "99",
      "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
      "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
      "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
      "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
      "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
      "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
      "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
      "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
      "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
      "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
      "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
      "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
      "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
      "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
      "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
      "250", "251", "252", "253", "254", "255",
  };

  std::string_view sv = table[x];

  std::memcpy(p, sv.data(), sv.size());
  return p + sv.size();
}
// credit: Peter Dimov
std::string ipv52(const uint64_t address) noexcept {
  std::string output(4 * 3 + 3, '\0');
  char *p = output.data();

  p = to_chars_52(p, uint8_t(address >> 24));
  *p++ = '.';
  p = to_chars_52(p, uint8_t(address >> 16));
  *p++ = '.';
  p = to_chars_52(p, uint8_t(address >> 8));
  *p++ = '.';
  p = to_chars_52(p, uint8_t(address >> 0));

  output.resize(p - output.data());
  return output;
}

代码在这里

https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2023/02/01/str.cpp

CPU角度省内存技巧,使用上这些技巧,你的代码搞不好变慢,各种加fence。说实话,没看懂意图

介绍UBSan的方方面面使用

老文,但挺有意思。直接贴代码了。原理去原文看吧

// should be much more precise with large b
inline double fastPrecisePow(double a, double b) {
  // calculate approximation with fraction of the exponent
  int e = (int) b;
  union {
    double d;
    int x[2];
  } u = { a };
  u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447);
  u.x[0] = 0;

  // exponentiation by squaring with the exponent's integer part
  // double r = u.d makes everything much slower, not sure why
  double r = 1.0;
  while (e) {
    if (e & 1) {
      r *= a;
    }
    a *= a;
    e >>= 1;
  }

  return r * u.d;
}

#include <iostream>

int main() {
    for (auto x : {"hello", "coding", "world"})
        std::cout << x << ", ";
}

等价于

#include <iostream>

int main()
{
  {
    const char *const __list21[3]{"hello", "coding", "world"};
    std::initializer_list<const char *> && __range1 
                            = std::initializer_list<const char *>{__list21, 3};
    const char *const * __begin1 = __range1.begin();
    const char *const * __end1 = __range1.end();
    for(; __begin1 != __end1; ++__begin1) {
      const char * x = *__begin1;
      std::operator<<(std::operator<<(std::cout, x), ", ");
    }
    
  }
  return 0;
}

std::initializer_list不能细想,越想越复杂

演示代码。说实话没怎么看懂

介绍 std::regular 的 看代码就懂了

template<class T>
concept movable = is_object_v<T> && move_constructible<T> &&
assignable_from<T&, T> && swappable<T>;

template<class T>
concept copyable = copy_constructible<T> && movable<T> && assignable_from<T&, const T&>;

template<class T>
concept semiregular = copyable<T> && default_constructible<T>;

template<class T>
concept regular = semiregular<T> && equality_comparable<T>;

满足复制交换构造赋值构造复制构造默认构造的同时支持比较

师父别念了

看代码

struct foo {
    int a{};
    int b{};
};

struct bar {
    const int x{};
    int y{};
};

struct baz : bar { };

struct other {
    int a{};
    char b[4]{};
};

static_assert(not std::is_layout_compatible_v<void, int>);
static_assert(not std::is_layout_compatible_v<const int*, const int&>);

static_assert(std::is_layout_compatible_v<const int, int const volatile>);
static_assert(std::is_layout_compatible_v<foo, bar>);
static_assert(std::is_layout_compatible_v<foo, baz>);
static_assert(std::is_layout_compatible_v<bar, baz>);
static_assert(not std::is_layout_compatible_v<bar, other>);
static_assert(not std::is_layout_compatible_v<bar, void>);

libcurl gumbo使用教学

关于MLIR的资料不多,这个小伙可以关注关注,大家感兴趣的可以看看

测了一下成员函数影响二进制大小,结论就是虚函数影响大,最好没成员函数。我感觉这个结论很直观,不用测也能猜到


Raymong Chen分区

最近都是winrt和协程的, 我不懂winrt,没啥说的

觉得main过于繁琐, 参数不好用,难以理解,给了个解决例子

#include <array>
#include <span>
#include <string_view>


constexpr std::span<std::string_view> start(int argc, char** argv) {
  static std::array<std::string_view, 255> argvElements = []() -> std::array<std::string_view, 255> {
    std::array<std::string_view, 255> argvElements;
    for (std::size_t i = 0; i < argc; ++i) {
      argvElements[i] = argv[i];
    }
    return argvElements;
  }();

  return argvElements;
}

int better_main(std::span<std::string_view> args);


int main(int argc, const char *argv[]) {
  return start(argc, argc, better_main);
}

更多讨论,看这里 https://github.com/lefticus/cpp_weekly/issues/209

我怎么感觉加了这一坨更难理解了?我觉得还是不要浪费时间放在这玩意上了。

开源项目需要人手

新项目介绍/版本更新

工作招聘

有没有需要拖地的,我会


本文永久链接

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

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