Skip to content

Releases: wanghenshui/cppweeklynews

C++ 中文周刊 第125期

04 Aug 14:20
bb2e37e
Compare
Choose a tag to compare

资讯

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

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

文章

写的不错

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> r = {1, 2, 3, 4, 5};
    auto reversed = r | std::views::reverse;
    for (auto i : reversed)
        std::cout << i << " ";
        
    // same as:
    //for (auto i : r | std::views::reverse)
        //std::cout << i << " ";
    
    std::cout << '\n';
    std::ranges::reverse_view rv(r);
    for (auto i : rv)
        std::cout << i << " ";
}

探讨这玩意是怎么实现的 #include
#include
#include

int main() {
std::vector r = {1, 2, 3, 4, 5};
auto reversed = r | std::views::reverse;
for (auto i : reversed)
std::cout << i << " ";

// same as:
//for (auto i : r | std::views::reverse)
    //std::cout << i << " ";

std::cout << '\n';
std::ranges::reverse_view rv(r);
for (auto i : rv)
    std::cout << i << " ";

}

帮你把结构体组成的数组这种行存转换为数组结构体模式列存

是代码帮你生成tuple。有点繁琐

static_assert(false, std::string_view{"message"});

既然能string_view 也就可以fmt

没啥说的。能省点空间

template<typename T1, typename T2>
struct compressed_pair {
    [[no_unique_address]] T1 first;
    [[no_unique_address]] T2 second;
};

介绍string对小字符串做的优化,类似clang::string的实现分析

光速入门

亮点还是扩容不需要迁移,很有意思

看不懂

视频

meeting cpp推荐了十个,我把我感兴趣的列一下

Daniel Withopf - Compile-time Is the New Constexpr: Leveraging Compile-time Sparsity for Vectors and Matrices

大概意思是利用constexpr算某些场景比elgen快

Filipp Gelman - What I learned From Sockets

讲了一堆设计。没有代码,听个乐

开源项目需要人手

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

新项目介绍/版本更新

std::embed没有之前能对付用的替代品

这哥们写了个find,和bfs一个原理,并发宽度优先遍历,find默认是dfs。

实际效果比find快百倍。很有意思。可以装一个玩玩

一个fat pointer实现

一个实现了work steal的线程池

关于 work steal,道理都懂,可能还需要看看https://www.youtube.com/watch?v=iLHNF7SgVN4&ab_channel=CppCon 了解下

工作招聘

有没有可以远程的工作可以推荐给我,我也快失业了

API Design

最近群里收集了一些想要讨论的点子,大家比较关注API设计,我这里把一些资料放在这里。可能后面会做个视频

https://www.youtube.com/watch?v=zL-vn_pGGgY&ab_channel=CppCon

https://www.youtube.com/watch?v=2UmDvg5xv1U&ab_channel=CppNow

https://www.acodersjourney.com/top-25-cplusplus-api-design-mistakes-and-how-to-avoid-them/


本文永久链接

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

C++ 中文周刊 第124期

28 Jul 14:46
Compare
Choose a tag to compare

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

提交 issue

感谢 振羽 不语 赞助


资讯

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

七月邮件列表

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/#mailing2023-07

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

文章

有点意思

struct foo {
  auto bar(int v) { return v; }
};

static_assert(42 == std::bind_front<&foo::bar>(foo{}, 42));

不懂啥意思

讲浮点数压缩的,没看懂。这里标记个TODO后面研究一下

SIMD时间,这回不贴代码了。https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/2023/07/27

作者想把

constexpr std::tuple<int, double, int, double, float> { 1, 2.0, 1, 3.0, 2.0 }

变成 constexpr std::tuple<int, double, double, float> { 1, 2.0, 3.0, 2.0 }
简单方案就是boost::mp_list,或者看这个 https://stackoverflow.com/questions/55941964/how-to-filter-duplicate-types-from-tuple-c

但作者想要的是如果值相等才把类型吃掉,有点点难

直接贴代码吧,我看不懂,作者推导了半天

#include <functional>
#include <tuple>

template<typename T1, typename T2>
consteval bool JMEq(const T1& v1, const T2& v2) {
	if constexpr (!std::is_same_v<T1, T2>)
		return false;
	else
		return v1 == v2;
}

template<const auto& F>
constexpr auto Nub() {
	constexpr auto tup = F();
	constexpr auto indices = std::make_index_sequence<std::tuple_size_v<decltype(tup)>> {};

	return [&]<std::size_t... Ix>(std::index_sequence<Ix...>)
	{
		return std::tuple_cat ([&]
			{
				constexpr auto index = Ix;
				constexpr auto element = std::get<index>(tup);

				if constexpr (((JMEq(element, std::get<Ix>(tup)) && Ix < index) || ...))
					return std::tuple {};
				else
					return std::tuple { element };
			} ()...);
	} (indices);
}

constexpr auto structuralize(auto tuple){
	return std::apply([]<typename... Args>(Args... args) { return ST<Args...>(args...); }, tuple);
}

constexpr std::tuple<int, double, int, double, float> input { 1, 2.0, 1, 3.0, 2.0 };
constexpr std::tuple<double, int, double, float> expected { 2.0, 1, 3.0, 2.0 };
constexpr auto actual = Nub<structuralize(input)>();
static_assert(expected == actual);

简单说就是lambda是对象,有时候不捕获的lambda也是对象,和函数指针差不多,太浪费了,于是引入了static lambda,static operator

auto isEven = [](int i) static {return i % 2 == 0;};

如果捕获会报错

// ERROR: 'static' lambda specifier with lambda capture
auto isDivisableBy = [operand](int i) static {return i % operand == 0;};

其实static operator[]原因也差不多。代码就不列举了

Perfect forwarding forwards objects, not braced things that are trying to become objects

forward对于 initializer_list对象行不通,initializer_list真该死啊

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args){
    return std::unique_ptr<T>(
        new T(std::forward<Args>(args)...));
}

这样就不行

struct Point {
    int x, y;
};

struct Segment {
    Segment(Point p1, Point p2);
};

void test() {
    // This works
    Segment s({ 1, 1 }, { 2, 2 });

    // This doesn't
    auto p = std::make_unique<Segment>(
        { 1, 1 }, { 2, 2 });
}

封装一层吧

struct Segment {
    Segment(Point p1, Point p2);
    template<typename Arg1 = Point,
             typename Arg2 = Point>
    static std::unique_ptr<Segment> make_unique(
        Arg1&& p1, Arg2&& p2) {
        return std::make_unique<Segment>(
            std::forward<Arg1>(p1),
            std::forward<Arg2>(p2));
    }
};

这样就行了

讲winrt的。不说了

视频

本周视频很多 cppnow 2023来了。基本上讲的是今年cppcon的前瞻内容

这个是之前他写的博客,直接做成视频讲了一遍,就是讲用tag dispatch替换switch加速的

ppt在这里 https://github.com/boostcon/cppnow_presentations_2023/blob/main/cppnow_slides/A_Deep_dive_into_dispatching_techniques.pdf

周末有空我就传一下

这个华人哥们讲的也有点意思

介绍numa的,有点意思

敏感字符串过滤?hash绕过

感觉之前说过,还是布局之类的。没有细看

这个教程也不错,手把手带你了解协程以及一个task模型

讲高频交易的,很干货。值得一看

讲基于epoch的内存回收的。epoch推进技术其实已经不是新东西了。到处都可见,或多或少要了解一下。了解背景之后值得看看

开源项目需要人手

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

新项目介绍/版本更新

工作招聘


本文永久链接

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

C++ 中文周刊 第123期

24 Jul 08:56
3ad8088
Compare
Choose a tag to compare

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

提交 issue

本周内容不多


资讯

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

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

文章

Inside boost::concurrent_flat_map

boost 1.83会有个 boost::concurrent_flat_map, 这篇文章带你了解设计思路。还是开地址法,还并发,还快

测了一下是吊锤tbb的。没测和folly::concurrentHashMap的比较

其实这个文章可以展开讲讲

浏览器安卓端崩溃最终怀疑是CPU的问题

总之就是利用CPU流水线先走一个快速路径,再检查条件,也就是所谓的分支预测

你可能想问了,我能不能让CPU别预测,别显得你多聪明了老老实实算就完了

__builtin_unpredictable() https://clang.llvm.org/docs/LanguageExtensions.html#builtin-unpredictable

基于async_simple的。有点意思

了解一下PGO流程

看个热闹

手把手教你写个grpc server

SIMD时间,字符转数字

常规, 一个一个比

if (ch >= '0' && ch <= '9')
  d = ch - '0';
else if (ch >= 'A' && ch <= 'V')
  d = ch - 'A' + 10;
else if (ch >= 'a' && ch <= 'v')
  d = ch - 'a' + 10;
else
  return -1;

进化版本,唉我会打表了

size_t base32hex_simple(uint8_t *dst, const uint8_t *src) {
  static const uint8_t table[256] = {
      32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
      32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
      32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
      0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  32, 32, 32, 32, 32, 32,
      32, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
      25, 26, 27, 28, 29, 30, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32,
      32, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
      25, 26, 27, 28, 29, 30, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32,

      32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
      32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
      32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
      32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
      32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
      32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
      32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
      32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
  };
  bool valid = true;
  const uint8_t *srcinit = src;

  do {
    uint64_t r = 0;
    for (size_t i = 0; i < 8; i++) {
      uint8_t x = table[*src];
      src++;
      if (x > 31) {
        r <<= (5 * (8 - i));
        valid = false;
        break;
      }
      r <<= 5;
      r |= x;
    }
    r = (unsigned long int)_bswap64((long long int)r);
    uint64_t rs = ((uint64_t)r >> (3 * 8));
    memcpy(dst, (const char *)&rs, 8);
    dst += 5;
  } while (valid);
  return (size_t)(src - srcinit);
}

超神版本 SIMD直接贴代码

size_t base32hex_simd(uint8_t *dst, const uint8_t *src) {
  bool valid = true;
  const __m128i delta_check =
      _mm_setr_epi8(-16, -32, -48, 70, -65, 41, -97, 9, 0, 0, 0, 0, 0, 0, 0, 0);
  const __m128i delta_rebase =
      _mm_setr_epi8(0, 0, 0, -48, -55, -55, -87, -87, 0, 0, 0, 0, 0, 0, 0, 0);
  const uint8_t *srcinit = src;
  do {
    __m128i v = _mm_loadu_si128((__m128i *)src);

    __m128i hash_key = _mm_and_si128(_mm_srli_epi32(v, 4), _mm_set1_epi8(0x0F));
    __m128i check = _mm_add_epi8(_mm_shuffle_epi8(delta_check, hash_key), v);
    v = _mm_add_epi8(v, _mm_shuffle_epi8(delta_rebase, hash_key));
    unsigned int m = (unsigned)_mm_movemask_epi8(check);

    if (m) {
      int length = __builtin_ctz(m);
      if (length == 0) {
        break;
      }
      src += length;
      __m128i zero_mask =
          _mm_loadu_si128((__m128i *)(zero_masks + 16 - length));
      v = _mm_andnot_si128(zero_mask, v);
      valid = false;
    } else { // common case
      src += 16;
    }
    v = _mm_maddubs_epi16(v, _mm_set1_epi32(0x01200120));
    v = _mm_madd_epi16(
        v, _mm_set_epi32(0x00010400, 0x00104000, 0x00010400, 0x00104000));
    // ...00000000`0000eeee`efffffgg`ggghhhhh`00000000`aaaaabbb`bbcccccd`dddd0000
    v = _mm_or_si128(v, _mm_srli_epi64(v, 48));
    v = _mm_shuffle_epi8(
        v, _mm_set_epi8(0, 0, 0, 0, 0, 0, 12, 13, 8, 9, 10, 4, 5, 0, 1, 2));

    /* decoded 10 bytes... but write 16 cause why not? */
    _mm_storeu_si128((__m128i *)dst, v);
    dst += 10;
  } while (valid);

  return (size_t)(src - srcinit);
}

还有SWAR版本,我直接贴仓库连接,不贴代码了 https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2023/07/20/src/base32.c

raymond chen的window时间,看不懂

开源项目需要人手

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

新项目介绍/版本更新


本文永久链接

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

C++ 中文周刊 第122期

14 Jul 13:45
da1c35c
Compare
Choose a tag to compare

周刊项目地址

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

提交 issue


资讯

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

#include cleanup in Visual Studio

支持提示删除没用的头文件,我记得clion是不是早就支持了?

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

Xmake v2.8.1 发布,大量细节特性改进

支持grpc-plugin了,那么别的支持也能实现,感觉更强了,可以用大项目试试

文章

auto $dollar_sign = 42;
auto @commerical_at = 42;
auto `grave_accent = 42;

没懂有啥用

  • 字符串截断问题

在某群和群友讨论 std::string能不能存带 '/0'的字符,我之前遇到过坑,就想当然的认为不能,然后被群友教育了一种用法

#include <iostream>
#include <vector>
#include <string>

int main() {
    std::string s1 = std::string{"\0\0\0\0\0\0\0\0\0\0\0\0\0\0123"};
    std::cout<<"---\n";
    std::cout<< s1.size()<<"\n";
    std::cout<<"---\n";
    std::string s2;
    s2.append("\0");
    s2.append("1");
    std::cout<<"---\n";
    std::cout<< s2.size()<<"\n";
    std::cout<< s2<<"\n";
    std::cout<<"---\n";

    std::vector<char> v = {'\0','\0','2','3','\0','5','6'};
    std::string s3(v.begin(), v.end());
    std::cout<<"---\n";
    std::cout<< s3.size()<<"\n";
    std::cout<< s3<<"\n";
    std::cout<<"---\n";
    std::string s4 = std::string{"\0\0\0\0\0\0\0\0\0\0\0\0\0\0a23", 17};
    std::cout<<"---\n";
    std::cout<< s4.size()<<"\n";
    std::cout<< s4<<"\n";
    std::cout<<"---\n";
    return 0;
}

s1 s2都是截断的,但是s3不是,想要std::string包含/0只能通过迭代器构造,不能从c字符串构造,因为c字符串复制判断结尾用的/0

同理,s4加上了长度构造,就包含/0不会截断了。c字符串的缺陷,没有长度信息

我这里先入为主觉得所有构造都这样了。稍为想的远一点

boost::concurrent_flat_map开链的并发hashmap速度不输tbb boost 1.83发布

有点意思

经典的友元函数注入

template<int N> struct tag{};

template<typename T, int N>
struct loophole_t {
  friend auto loophole(tag<N>) { return T{}; };
};
auto loophole(tag<0>);
sizeof(loophole_t<std::string, 0> );
statc_assert(std::is_same<std::string, decltype(loophole(tag<0>{})) >::value, "same");

这玩意属于缺陷,说不定以后就修了,为啥要讲这个老文章?看下面这个

看代码

//Conceptify it (Requires C++20)
struct Drawable {
  void draw(std::ostream &out) const {
    te::call([](auto const &self, auto &out)
      -> decltype(self.draw(out)) { self.draw(out); }, *this, out);
  }
};

struct Square {
  void draw(std::ostream &out) const { out << "Square"; }
};

template<te::conceptify<Drawable> TDrawable>
void draw(TDrawable const &drawable) { 
  drawable.draw(std::cout);
}

int main() {
  auto drawable = Square{};
  draw(drawable);  // prints Square
}

我咋感觉te::conceptify<Drawable>看上去和实现concept没区别?

这种实现能定义te::poly<Drawable>保存在容器里然后遍历?

实现原理是通过一个mapping类注册类型和typelist,typelist绑上lambda, 我说的不精确,可以看原文

call注册

template <...>
constexpr auto call(const TExpr expr, const I &interface, Ts &&... args) {
  ...
  return detail::call_impl<I>(...);
}

template <...>
constexpr auto call_impl(...) {
  void(typename mappings<I, N>::template set<type_list<TExpr, Ts...> >{});
  return ...;
}

mapping是这样的

template <class, std::size_t>
struct mappings final {
  friend auto get(mappings);
  template <class T>
  struct set {
    friend auto get(mappings) { return T{}; }
  };
};

通过这个友元注入get 来记住T类型,也就是之前保存的typelist,构造出lambda

template <class T, class TExpr, class... Ts>
constexpr auto requires_impl(type_list<TExpr, Ts...>)
    -> decltype(&TExpr::template operator()<T, Ts...>);

template <class I, class T, std::size_t... Ns>
constexpr auto requires_impl(std::index_sequence<Ns...>) -> type_list<
    decltype(requires_impl<I>(decltype(get(mappings<T, Ns + 1>{})){}))...>;
}  // namespace detail


template <class I, class T>
concept bool conceptify = requires {
  detail::requires_impl<I, T>(
      std::make_index_sequence<detail::mappings_size<T, I>()>{});
};

挺巧妙,友元函数注入+typelist绑定

问题在于lambda每次都是构造的,可能有小对象问题,但愿编译器能优化掉

find还要判断结果,很烦,类似optional,封装一下

#include <iostream>
#include <vector>
#include <algorithm>
#include <optional>

namespace cwt {    
    // first we craete a type which will hold our find result
    template<typename T> 
    class find_result {
        public: 
            find_result() = default;
            find_result(T value) : m_value{value} {}

            // this is the and_then method which gets a callback
            template<typename Func>
            const find_result<T>& and_then(const Func&& func) const {
                // we only call the callback if we have a value 
                if (m_value.has_value()) {
                    func(m_value.value());
                }
                // and to further add or_else we return *this
                return *this;
            }

            // almost same here, just with return type void
            template<typename Func>
            void or_else(const Func&& func) const {
                if (!m_value.has_value()) {
                    func();
                }
            }
        private:
            // and since we don't know if we found a value
            // we hold possible one as optional
            std::optional<T> m_value{std::nullopt}; 
    };

    // this my find function, where try to find a value in a vector
    template<typename T>
    find_result<T> find(const std::vector<T>& v, const T value) {
        // like before we use the iterator
        auto it = std::find(v.begin(), v.end(), value);
        // and if we didn't find the value we return
        // find_result default constructed
        if (it == v.end()) {
            return find_result<T>();
        } else {
            // or with the found value
            return find_result<T>(*it);
        }
    }
} // namespace cwt

int main() {
    // lets create a simple vector of int values
    std::vector<int> v = {1,2,3,4};

    // we use our find function
    // and since we return find_result<int> 
    // we can append or call and_then or_else directly
    cwt::find(v, 2)
    .and_then([](int result){ std::cout << "found " << result << '\n'; })
    .or_else([](){ std::cout << "found nothing\n"; })
    ;

    cwt::find(v, 10)
    .and_then([](int result){ std::cout << "found " << result << '\n'; })
    .or_else([](){ std::cout << "found nothing\n"; })
    ;

    return 0;
}

看个乐

SIMD环节,需求,把 "20141103 012910"转成数字0x20141103012910

#include <x86intrin.h> // Windows: <intrin.h>
#include <string.h>

// From "20141103 012910", we want to get
// 0x20141103012910
uint64_t extract_nibbles(const char* c) {
  uint64_t part1, part2;
  memcpy(&part1, c, sizeof(uint64_t));
  memcpy(&part2 , c + 7, sizeof(uint64_t));
  part1 = _bswap64(part1);
  part2 = _bswap64(part2);
  part1 = _pext_u64(part1, 0x0f0f0f0f0f0f0f0f);
  part2 = _pext_u64(part2, 0x0f000f0f0f0f0f0f);
  return (part1<<24) | (part2);
}

汇编

movbe rax, QWORD PTR [rdi]
movbe rdx, QWORD PTR [rdi+7]
movabs rcx, 1085102592571150095
pext rax, rax, rcx
movabs rcx, 1080880467920490255
sal rax, 24
pext rdx, rdx, rcx
or rax, rdx

pext在amd zen3架构上开销很大,但本身也非常小巧了

ARM平台

#include <arm_neon.h>
// From "20141103 012910", we want to get
// 0x20141103012910
uint64_t extract_nibbles(const char *c) {
  const uint8_t *ascii = (const uint8_t *)(c);
  uint8x16_t in = vld1q_u8(ascii);
  // masking the high nibbles,
  in = vandq_u8(in, vmovq_n_u8(0x0f));
  // shuffle the bytes
  const uint8x16_t shuf = {14, 13, 12, 11, 10, 9, 7, 6,
    5, 4, 3, 2, 1, 0, 255, 255};
  in = vqtbl1q_u8(in, shuf);
  // then shift/or
  uint16x8_t ins =
    vsraq_n_u16(vreinterpretq_u16_u8(in),
    vreinterpretq_u16_u8(in), 4);
  // then narrow (16->8),
  int8x8_t packed = vmovn_u16(ins);
  // extract to general register.
  return vget_lane_u64(vreinterpret_u64_u16(packed), 0);
}

汇编

adrp x8, .LCPI0_0
ldr q1, [x0]
movi v0.16b, #15
ldr q2, [x8, :lo12:.LCPI0_0]
and v0.16b, v1.16b, v0.16b
tbl v0.16b, { v0.16b }, v2.16b
usra v0.8h, v0.8h, #4
xtn v0.8b, v0.8h
fmov x0, d0

SIMD环节,在一个字符串数组里找子串是否存在

普通实现,bsearsh

std::string *lookup_symbol(const char *input) {
  return bsearch(input, strings.data(), strings.size(),
  sizeof(std::string), compare);
}

或者trie

或者SIMD

代码我就不贴了,https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2023/07/13/benchmarks/benchmark.cpp

天书

参考阅读 https://trent.me/is-prefix-of-string-in-table/ 天书

这个讲的是局部性问题

比如

class my_class {
   int a;
   int b;
   ...
   int z;
};
int sum_all(my_class* m, int n) {
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += m[i].a + m[i].z;
...
Read more

C++ 中文周刊 第121期

07 Jul 14:33
6a44111
Compare
Choose a tag to compare

周刊项目地址

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

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

提交 issue

本周内容不多


资讯

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

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

文章

虽然是老文了,对于清晰概念还是有一定的帮助的。讨论了很多基础概念,以及各种时序场景,我觉得都可以复述一遍这个演讲,增加自己理解

能讲给别人进步最快,各位可能都讲过题,要不要挑战一下

template <template <class...> class TList, class TEvent, class... TEvents, class T, class TExpr>
constexpr auto dispatch(TList<TEvent, TEvents...>, const int id, const T& data,
                        const TExpr& expr) -> decltype(expr(TEvent{data})) {
    switch (id) {
        case TEvent::id:
            return expr(TEvent{data});

        default:
            if constexpr (sizeof...(TEvents) > 0) {
                return dispatch(TList<TEvents...>{}, id, data, expr);
            }
    }
    return {};
}

不太懂有啥用

overload谁都会写,但是可能存在隐式转换,比如

template<class... Ts>
struct overload : Ts... {
  using Ts::operator()...;
};

template<class... Ts>
overload(Ts...) -> overload<Ts...>;

int main() {
  const std::variant<int, bool> v{true};
  std::visit(
      overload{
          [](int val) { std::cout << val; },
          [](bool val) { std::cout << std::boolalpha << val; }, // 1
      },
      v);
}

这里的int bool模糊,可能发生转换。如果把1这行注释掉,visit一样能遍历,走int哪个分支。发生转换了我超!

如何杀死这种异常?

template<class...>
constexpr bool always_false_v = false;

template<class... Ts>
struct overload : Ts... {
  using Ts::operator()...;

  template<typename T>
  constexpr void operator()(T) const {
    static_assert(always_false_v<T>, "Unsupported type");
    // c++23 static_assert(false, "Unsupported type");
  }
};

template<class... Ts>
overload(Ts...) -> overload<Ts...>;

int main() {
  const std::variant<int, bool> v{true};

  std::visit(overload{
                 [](int val) { std::cout << val; },
                 [](bool val) { std::cout << std::boolalpha << val; },
             },
             v);
}

正常走Ts的operator(),如果类型不匹配(隐式转换的前提是没有别的实现),最佳匹配是static_assert的那个实现,匹配中,编译报错

自己的overload都改一下,这个还是值得一用的

这个问题其实和上面差不多,指针有隐式转换成bool的风险,可能一不小心写出bug

你比如有个类是这样的

class string_literal {
public:
    operator const char*() const noexcept {
        return m_ptr;
    }
};

问题来了,char*转bool

int main() {
    string_literal str;

    if (str) {} //char* 转bool我超

    str + 1; // char*取偏移我超
}

这种代码,如果真的有bug被引入,都不想琢磨,太恶心了

怎么修?限制转换,强制约束类型,和上面的方案不谋而合

class string_literal{
public:
    template <std::same_as<const char*> T>
    operator T() const noexcept
    {
        return m_ptr;
    }
};

c++20的concept感觉没有大规模推开,大家为了旧代码没升级,没用上

代码在这里 https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2023/07/01/src/sse_date.c

解析时间,常规写法

bool parse_time(const char *date_string, uint32_t *time_in_second) {
  const char *end = NULL;
  struct tm tm;
  if ((end = strptime(date_string, "%Y%m%d%H%M%S", &tm)) == NULL) {
    return false;
  }
  *time_in_second = (uint32_t)mktime_from_utc(&tm);
  return true;
}

SSE代码我看不懂,直接贴了

bool sse_parse_time(const char *date_string, uint32_t *time_in_second) {
  // We load the block of digits. We subtract 0x30 (the code point value of the
  // character '0'), and all bytes values should be between 0 and 9,
  // inclusively. We know that some character must be smaller that 9, for
  // example, we cannot have more than 59 seconds and never 60 seconds, in the
  // time stamp string. So one character must be between 0 and 5. Similarly, we
  // start the hours at 00 and end at 23, so one character must be between 0
  // and 2. We do a saturating subtraction of the maximum: the result of such a
  // subtraction should be zero if the value is no larger. We then use a special
  // instruction to multiply one byte by 10, and sum it up with the next byte,
  // getting a 16-bit value. We then repeat the same approach as before,
  // checking that the result is not too large.
  //
  __m128i v = _mm_loadu_si128((const __m128i *)date_string);
  // loaded YYYYMMDDHHmmSS.....
  v = _mm_xor_si128(v, _mm_set1_epi8(0x30));
  // W can use _mm_sub_epi8 or _mm_xor_si128 for the subtraction above.
  // subtracting by 0x30 (or '0'), turns all values into a byte value between 0
  // and 9 if the initial input was made of digits.
  __m128i limit =
      _mm_setr_epi8(9, 9, 9, 9, 1, 9, 3, 9, 2, 9, 5, 9, 5, 9, -1, -1);
  // credit @aqrit
  // overflows are still possible, if hours are in the range 24 to 29
  // of if days are in the range 32 to 39
  // or if months are in the range 12 to 19.
  __m128i abide_by_limits = _mm_subs_epu8(v, limit); // must be all zero

  __m128i byteflip = _mm_setr_epi64((__m64)0x0607040502030001ULL,
                                    (__m64)0x0e0f0c0d0a0b0809ULL);

  __m128i little_endian = _mm_shuffle_epi8(v, byteflip);
  __m128i limit16 = _mm_setr_epi16(0x0909, 0x0909, 0x0102, 0x0301, 0x0203,
                                   0x0509, 0x0509, -1);
  __m128i abide_by_limits16 =
      _mm_subs_epu16(little_endian, limit16); // must be all zero

  __m128i combined_limits =
      _mm_or_si128(abide_by_limits16, abide_by_limits); // must be all zero
  // We want to disallow 0s for days and months... and we want to make
  // sure that we don't go back in time prior to 1900.
  __m128i limit16_low = _mm_setr_epi16(0x0109, 0, 0x0001, 0x0001, 0, 0, 0, 0);

  __m128i abide_by_limits16_low =
      _mm_subs_epu16(limit16_low, little_endian); // must be all zero
  combined_limits = _mm_or_si128(combined_limits, abide_by_limits16_low);

  if (!_mm_test_all_zeros(combined_limits, combined_limits)) {
    return false;
  }
  // 0x000000SS0mmm0HHH`00DD00MM00YY00YY
  //////////////////////////////////////////////////////
  // pmaddubsw has a high latency (e.g., 5 cycles) and is
  // likely a performance bottleneck.
  /////////////////////////////////////////////////////
  const __m128i weights = _mm_setr_epi8(
      //     Y   Y   Y   Y   m   m   d   d   H   H   M   M   S   S   -   -
      10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 0, 0);
  v = _mm_maddubs_epi16(v, weights);

  uint64_t hi = (uint64_t)_mm_extract_epi64(v, 1);
  uint64_t seconds = (hi * 0x0384000F00004000) >> 46;
  uint64_t lo = (uint64_t)_mm_extract_epi64(v, 0);
  uint64_t yr = (lo * 0x64000100000000) >> 48;

  // We checked above that dy and mo are >= 1
  uint64_t mo = ((lo >> 32) & 0xff) - 1;
  uint64_t dy = (uint64_t)_mm_extract_epi8(v, 6);

  bool is_leap_yr = is_leap_year((int)yr);

  if (dy > (uint64_t)mdays[mo]) { // unlikely branch
    if (mo == 1 && is_leap_yr) {
      if (dy != 29) {
        return false;
      }
    } else {
      return false;
    }
  }
  uint64_t days = 365 * (yr - 1970) + (uint64_t)leap_days(1970, (int)yr);

  days += (uint64_t)mdays_cumulative[mo];
  days += is_leap_yr & (mo > 1);

  days += dy - 1;
  uint64_t time_in_second64 = seconds + days * 60 * 60 * 24;
  *time_in_second = (uint32_t)time_in_second64;
  return time_in_second64 == (uint32_t)time_in_second64;
}


static const int mdays_minus_one[] = {30, 27, 30, 29, 30, 29, 30, 30, 29, 30, 29, 30};

// uses more instructions than sse_parse_time but might be slightly faster.
bool sse_parse_time_alt(const char *date_string, uint32_t *time_in_second) {
  // We load the block of digits. We subtract 0x30 (the code point value of the
  // character '0'), and all bytes values should be between 0 and 9,
  // inclusively. We know that some character must be smaller that 9, for
  // example, we cannot have more than 59 seconds and never 60 seconds, in the
  // time stamp string. So one character must be between 0 and 5. Similarly, we
  // start the hours at 00 and end at 23, so one character must be between 0
  // and 2. We do a saturating subtraction of the maximum: the result of such a
  // subtraction should be zero if the value is no larger. We then use a special
  // instruction to multiply one byte by 10, and sum it up with the next byte,
  // getting a 16-bit value. We then repeat the same approach as before,
  // checking that the result is not too large.
  //
  // We compute the month the good old ways, as an integer in [0,11], we
  // check for overflows later.
  uint64_t mo = (uint64_t)((date_string[4]-0x30)*10 + (date_string[5]-0x30) - 1);
  __m128i v = _mm_loadu_si128((const __m128i *)date_string);
  // loaded YYYYMMDDHHmmSS.....
  v = _mm_xor_si128(v, _mm_set1_epi8(0x30));
  // W can use _mm_sub_epi8 or _mm_xor_si128 for the subtraction above.
  // subtracting by 0x30 (or '0'), turns all values into a byte value between 0
  // and 9 if the initial input was made of digits.
  __m128i limit =
      _mm_setr_epi8(9, 9, 9, 9, 1, 9, 3, 9, 2, 9, 5, 9, 5, 9, -1, -1);
  // credit @aqrit
  // overflows are still possible, if hours are in the range 24 to 29
  // of if days are in the range 32 to 39
  // or if months are in the range 12 to 19.
  __m128i abide_by_limits = _mm_subs_epu8(v, limit); // must be all zero

  __m1...
Read more

C++ 中文周刊 第120期

30 Jun 15:11
641ae17
Compare
Choose a tag to compare

周刊项目地址

公众号

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

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

提交 issue

感谢不语赞助


资讯

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

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

文章

int main() {
  using v4si = int [[gnu::vector_size(4 * sizeof(int))]];

  v4si a = {1, 2, 3, 4};
  v4si b = {4, 3, 2, 1};
  v4si c;

  c = a + b;
  std::cout << c[0] << c[1] << c[2] << c[3]; // prints 5555
}

这玩意是给simd方便的。看gcc样例

#include <immintrin.h>

typedef unsigned char u8x16 __attribute__ ((vector_size (16)));
typedef unsigned int  u32x4 __attribute__ ((vector_size (16)));

typedef union {
        __m128i mm;
        u8x16   u8;
        u32x4   u32;
} v128;

v128 x, y = { 0 };
memcpy (&x, ptr, sizeof x);
y.u8  += 0x80;
x.mm  = _mm_adds_epu8 (x.mm, y.mm);
x.u32 &= 0xffffff;

/* Instead of a variable, a compound literal may be used to pass the
   return value of an intrinsic call to a function expecting the union: */
v128 foo (v128);
x = foo ((v128) {_mm_adds_epu8 (x.mm, y.mm)});

这个debug宏很好用,方便阅读,代码在这里 https://github.com/nosql-cn/AxeDB

不过既然已经编译了,通过clangd 应该也能抓到堆栈。不过静态的堆栈没有这种运行时堆栈有意义,运行时的更直观一些

知乎有人翻译成中文了,挺好。 https://zhuanlan.zhihu.com/p/639886110

原理就是std::variant + std::visit

看不懂

了解一波

代码走读,感兴趣的可以看看

还是讲他的relocatable提案

介绍vs上体验AddressSanitizer新特性COE(continue_on_error)

-fsanitizer=address 同时设置

    set ASAN_OPTIONS=continue_on_error=1
    set ASAN_OPTIONS=continue_on_error=2

打散一个64字节的数的场景

uint64_t w = some value;
uint8_t indexes[64] = {63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51,
                       50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38,
                       37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25,
                       24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12,
                       11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
bit_shuffle(w, indexes); // returns a reversed version 

可能的实现

uint64_t slow_bit_shuffle(uint64_t w, uint8_t indexes[64]) {
  uint64_t out{};
  for (size_t i = 0; i < 64; i++) {
    bool bit_set = w & (uint64_t(1) << indexes[i]);
    out |= (uint64_t(bit_set) << i);
  }
  return out;
}

考虑avx512

uint64_t bit_shuffle(uint64_t w, uint8_t indexes[64]) {
  __mmask64 as_mask = _cvtu64_mask64(w);
  __m512i as_vec_register =
  _mm512_maskz_mov_epi8(as_mask, _mm512_set1_epi8(0xFF));
  __m512i as_vec_register_shuf =
  _mm512_permutexvar_epi8(_mm512_loadu_si512(indexes), as_vec_register);
  return _cvtmask64_u64(_mm512_movepi8_mask(as_vec_register_shuf));
}

快点

uint64_t faster_bit_shuffle(uint64_t w, uint8_t indexes[64]) {
  __m512i as_vec_register = _mm512_set1_epi64(w);
  __mmask64 as_mask = _mm512_bitshuffle_epi64_mask(as_vec_register,
     _mm512_loadu_si512(indexes));
  return _cvtmask64_u64(as_mask);
}

有些芯片性能太好掩盖了一些代码优化的潜力,讲了几个场景 循环展开/pipeline对于老芯片带来的性能提升。观点挺有意思

这个博客 反复介绍过多次 https://johnnysswlab.com/ 推荐收藏没事看看

讲自己的演讲经历,如何演讲,找话题找组织,如何保证感兴趣,等等

话说,想组织一个c++讨论演讲,怎么才能组织起人来看,找到演讲者呢。

之前看祁宇(qicosmos)搞过,太正式了其实。类似meetingcpp这种网络会议直播模式就行

大家给给点子,我想整一个。没话题其实可以以国外视频转述一遍。加深理解。

raymond chen真能写。我一个没看

讲一些用法,尽量避免。没啥说的。有些有点搞笑。这里就不列举了

视频

讲constinit的。没啥说的。能用就用

这个还是很值得一看的,这个哥们是性能调优专家,有个博客搜索权重挺高 https://johnnysswlab.com/

这个改天我转b站上。或者谁有空给传一下

这个是技术债了,异常问题异常安全等等,我看不进去。这里标记个TODO改天整理一下观点(或者找chatgpt老师提取一下。。。)

代码https://sourceforge.net/p/libjmmcg/git-repo/ci/ACCUConf2023/tree/libjmmcg/

挺能high的,没看完 PPT https://github.com/wanghenshui/wanghenshui.github.io/blob/master/assets/ACCUConf2023_JMMcG_e0d2d_PRESENTED.pdf

ppt https://jamespascoe.github.io/accu2023/

看个乐

开源项目需要人手

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

新项目介绍/版本更新


本文永久链接

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

C++ 中文周刊 第119期

24 Jun 13:39
dbaa359
Compare
Choose a tag to compare

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

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

提交 issue

c++26进行中,一堆trip report

这里有个详细的帖子列举了所有进展 https://www.reddit.com/r/cpp/comments/14h4ono/202306_varna_iso_c_committee_trip_report_first/

还有一些trip report我就不复述内容了

我比较关注的还是fiber_context SIMD 这俩应该没啥问题。可以先写boost的代码,慢慢切。

以及std::execution到底行不行了,反射到底行不行?

另外,如果内容较少/我没空,可能会偶尔鸽一下,有没有小编感兴趣收集,或者一起搞搞,可以邮件wanghenshui@qq.com

本周内容不多


资讯

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

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

文章

还是c++26的玩意

作者提了他感兴趣的

[[maybe_unused]] auto [x, y, iDontCare] = f();

auto [x, y, _] = f(); // c++26

终于把这玩意加上了,go啥的都有

不过没有这玩意的时候,我都是_ = std::ignore; 然后这么用

Hazard Pointers 不多说,folly已经有了https://github.com/facebook/folly/blob/main/folly/synchronization/Hazptr.h

也可以看看这个 https://www.bilibili.com/video/BV1ha411k7pa?p=73

static_vector boost/llvm也有

Native Handles and File Streams c++库函数处理的fd吐出来,比如iostream这种

gcc14引入 -Wnrvo 帮助提醒优化返回值,P2025

代码patch在这里

https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=7e0b65b239c3a0d68ce94896b236b03de666ffd6

https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=28db36e2cfca1b7106adc8d371600fa3a325c4e2

这方面不如clang,话说clang早就上了

默认-std=gnu++17,带来一堆23的功能

放松static_assert

template<typename> void f() {
  static_assert (false, "");
}

没特化之前这里不报错,还是很有用的

放松 constexpr限制

constexpr char test () {
  static constexpr char c[] = "Hello World"; // OK in C++23
  return c[1];
}

static_assert (test () == 'e');

支持static operator

比如()

struct S {
  static constexpr bool operator() (int x, int y) { return x < y; }
};
constexpr S s;
static_assert (s (1, 2));

void g() {
  S::operator()(1, 2);  // OK in C++23
}

比如[]

struct S {
  S() {}
  static int& operator[]() { return mem[0]; }
  static int mem[64];
};

void g() {
  S s;
  s[]++;
}

浮点数增强

#include <stdfloat>

int main (){
  std::float16_t f16 = 1.0f16;
  std::float32_t f32 = 2.0f32;
  std::float64_t f64 = 3.0f64;
  std::float128_t f128 = 4.0f128;
 std::bfloat16_t x = 1.0bf16;
}

move简化, 这种代码gcc13编译不过了

int& g(int&& x) {
  return x; 
}

assume终于来了,contraits的一小部分能力

int foo (int x, int y) {
  [[assume (x >= y)]];
  if (x == y)
    return 0;
  else if (x > y)
    return 1;
  else
    return -1;
}

生成的汇编更简单

@@ -8,11 +8,7 @@ _Z3fooii:
.cfi_startproc
xorl    %eax, %eax
cmpl    %esi, %edi
-    je    .L1
-    setg    %al
-    movzbl    %al, %eax
-    leal    -1(%rax,%rax), %eax
-.L1:
+    setne    %al
ret
.cfi_endproc
 .LFE0:

代码在这里 https://github.com/tgfrerer/pal_tasks/tree/main

简单说,就是像调度future一样调度协程,把协程封装成task (不如seastar,看个乐也行)

讲future的

大意是别move返回值优化的编译器能处理好的场景

  enum class Suit{
  spades, hearts, diamonds, clubs };

  enum class Suit_with_joker extends Suit {
    joker };

想要这种语法,又能复用,又能安全,然后他用类继承糊了一个

struct Suit;
struct Suit_names
{
  static const Suit spades;
  static const Suit hearts;
};
struct Suit:
  Suit_names
{
  int value;
  constexpr explicit Suit( const int v )
    : value( v ) {}
};
constexpr Suit Suit_names::spades = Suit( 0 );
constexpr Suit Suit_names::hearts = Suit( 1 );

struct Suit_with_joker;
struct Suit_with_joker_names:
  Suit_names
{
  static const Suit_with_joker joker;
};
struct Suit_with_joker:
  Suit_with_joker_names
{
  int value;
  constexpr explicit
    Suit_with_joker( const int v ): value( v ) {}
  constexpr Suit_with_joker( const Suit v )
    : value( v.value ) {}
};
constexpr Suit_with_joker
  Suit_with_joker_names::joker
  = Suit_with_joker( 4 );
auto main() -> int
{
  (void) Suit_with_joker::hearts;
    // OK, has inherited the "enumerators".
  Suit_with_joker s1 = Suit::hearts;
    // OK, right way is-a relationship.
  #ifdef FAIL_PLEASE
    Suit s2 = Suit_with_joker::joker;
      //! C. error, /as it should be/. :)
  #endif
}

这种需求真的有么?不太值得

看不懂

视频

  • 字节开源高性能C++ JSON库sonic-cpp
<iframe src="//player.bilibili.com/player.html?aid=272293765&bvid=BV1nc411g7PK&cid=1165281732&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>

结合yyjson和simdjson,优化各自的缺陷,实现sonic-cpp,主要是rapid-json性能不rapid

相当于带你走读simdjson代码,教你看懂那一堆_mm函数,还是挺值得一看的,还讲了一些simdjson没有的工程细节,一些池化

能用就用,这玩意不如resize_uninit, 也不知道为啥非得整个lambda接口,服了

https://github.com/italiancpp/itcppcon23/blob/main/Lock-free%20micro%20problems%20-%20Davide%20Di%20Gennaro.pdf

有点意思

B站我也转了 https://www.bilibili.com/video/BV1fz4y1H7BD/

其实他说了半天,说的还是hazard pointer 不删除的那一套东西。不过也是一个视角

开源项目需要人手

新项目介绍/版本更新


本文永久链接

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

C++ 中文周刊 第118期

24 Jun 11:34
cb609de
Compare
Choose a tag to compare

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

提交 issue

本周内容不多 感谢不语赞助


资讯

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

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

文章

stdexec代码中的技巧

// #include <concepts>
#include <type_traits>

// struct compile_error<What, With...>
//=====================================
struct none;

template <class What, class... With>
struct compile_error : std::false_type 
{
    using type = compile_error;
};

template <>
struct compile_error<none> : std::true_type
{
    using type = compile_error;
};

// concept error<T>
// concept no_error<T>
//=====================================
namespace detail {

consteval compile_error<none> get_error(...);

template <class What, class... With>
consteval compile_error<What, With...> get_error(const compile_error<What, With...>*);

template <class T>
extern decltype(get_error((T*)nullptr)) error_v;

template <class T>
using error_t = decltype(error_v<T>);

template <class T>
concept error_impl = (not T{});

template <class T>
concept error = error_impl<error_t<T>>;

template <class T>
concept no_error_impl = T{};

template <class T>
concept no_error = no_error_impl<error_t<T>>;

} // namespace detail

using detail::error;
using detail::no_error;

// Usage
//=======

struct NOT_CALLABLE;

template <class T>
struct WITH_SIGNATURE;

template <class T>
struct is_function : std::conditional_t<std::is_function_v<T>, 
                                        std::true_type, 
                                        compile_error<NOT_CALLABLE, WITH_SIGNATURE<T>>>
{};

template <class T>
inline constexpr bool is_function_v = is_function<T>::value;

int add(int a, int b) {
    return a + b;
}

template <class T>
#if defined(USE_COMPILE_ERROR)
    requires no_error<is_function<T>>
#else
    requires is_function_v<T>
#endif
void foo(T f) {
}

int main() {
    foo<int(int, int)>(add);
    foo(add);
}

效果

效果

有点意思

介绍几个函数std::ranges::fold_left std::ranges::fold_left_with_iter std::ranges::fold_left_first_with_iter

贴个简单的

#include <iostream>
#include <algorithm>
#include <vector>

int main() {
    std::vector nums {1, 2, 3, 4};
    std::cout << std::ranges::fold_left(nums, 0, std::plus{});
}
/*
10
*/
#include <functional>

constexpr auto sum(auto... ts) { return (ts + ...); }
static_assert(typeid(int) == typeid(std::invoke_r<int>(&sum<short, short>, 3, 4)));

std::invoke的带返回值版本

googletest是怎么执行test的?一个全局map,把所有测试类字符串都全局注册到一个map里,其实就类似工厂函数,比如

static unique_ptr<ICompressionMethod> Create(const string& fileName) {
    auto extension = GetExtension(filename);
    if (extension == "zip")
        return make_unique<ZipCompression>();
    else if (extension = "bz")
        return make_unique<BZCompression>();

    return nullptr;
}

提前把zip bz注册好,这种写法肯定是不合适的,笨拙

gtest是这样的

#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\
class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
: public parent_class { \
  virtual void TestBody();\
  static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\
};\
\
::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\
  ::test_info_ =\
    ::testing::internal::MakeAndRegisterTestInfo(\
        #test_case_name, #test_name, NULL, NULL, \
        new ::testing::internal::TestFactoryImpl<\
            GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\
void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()

每个测试类都有个没啥用的static testinfo,用来执行注册MakeAndRegisterTestInfo

好了,来重构咱们的构造工厂

class ICompressionMethod {
public:
    ICompressionMethod() = default;
    virtual ~ICompressionMethod() = default;
    virtual void Compress() = 0;
};

template <typename Key, typename Value, size_t Size>
struct Map {
  std::array<std::pair<Key, Value>, Size> data;
  size_t slot_ { 0 };

    constexpr bool insert(const Key &key, const Value& val) {
        if (slot_ < Size) {
            data[slot_] = std::make_pair(key, val);
            ++slot_;
            return true;
        }
        return false;
    }

  [[nodiscard]] constexpr Value at(const Key &key, const Value& none) const {
    const auto itr =
        std::find_if(begin(data), end(data),
                     [&key](const auto &v) { return v.first == key; });
    if (itr != end(data)) {
      return itr->second;
    } else {
      return none;
    }
  }

};


class CompressionMethodFactory {
public:
    using TCreateMethod = unique_ptr<ICompressionMethod>(*)();
public:
    CompressionMethodFactory() = delete;

    static constexpr bool Register(string_view name, 
                                   TCreateMethod createFunc) {
        if (auto val = s_methods.at(name, nullptr); val == nullptr) {
            if (s_methods.insert(name, createFunc)) {
                std::cout << name << " registered\n";
                return true;
            }
       }
       return false;
    }
    static std::unique_ptr<ICompressionMethod> Create(string_view name) {
        if (auto val = s_methods.at(name, nullptr); val != nullptr) {
            std::cout << "calling " << name << "\n";
            return val();
        }
       return nullptr;
    }
private:    
    static inline constinit Map<string_view, TCreateMethod, 4> s_methods;
};

这样,类似testinfo调用Register来注册

class ZipCompression : public ICompressionMethod {
public:
    virtual void Compress() override;

    static unique_ptr<ICompressionMethod> CreateMethod() { 
        return std::make_unique<ZipCompression>(); 
    }
    static string_view GetFactoryName() { return "ZIP"; }
private:
    static inline bool s_registered = 
    CompressionMethodFactory::Register(ZipCompression::GetFactoryName(),
                                       CreateMethod);
};

这个注册可能存在依赖关系,而static初始化的顺序取决于编译单元被链接后的先后顺序?constinit来保证

代码在这里 https://wandbox.org/permlink/bO5epDpOhMH8NlXQ

class MyClass{
public:
  MyClass();
  // Not copyable.
  MyClass(const MyClass&) = delete;
  // Movable only for NRVO purposes (and RVO in C++11).
  // Never implemented.
  MyClass(MyClass&&);

  // Not assignable.
  void operator=(const MyClass&) = delete;
};

MyClass test1() {
    return MyClass(); // RVO
}

MyClass test2() {
    MyClass c;
    return c; // NRVO
}

MyClass test3() {
    MyClass c, d;
    if (some_condition()) {
        return c; // failed NRVO
    } else {
        return d; // failed NRVO
    }
}

看代码,这段代码展示的是编译器尽可能的做返回值优化,但test3做不了,可能走move优化,但move只声明没实现,报错

看各位的需求了,这种场景下实现move还是值得的

视频

没啥说的。std::thread经典例子就用到这玩意了 例子在这里 https://en.cppreference.com/w/cpp/thread/thread/thread 不贴代码了

都没啥意思,也就 Build Time Reflection with C++ in Year 2023 | Pure Virtual C++ 2023 有点意思

基于module的反射,之前他搞得ifc-spec 不过还没开源,倒是挺有意思

开源项目需要人手

  • asteria 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群384042845和作者对线
  • Unilang deepin的一个通用编程语言,点子有点意思,也缺人,感兴趣的可以github讨论区或者deepin论坛看一看。这里也挂着长期推荐了
  • gcc-mcf 废弃了对at&t汇编语法支持,仅支持intel汇编语法,话说at那种确实不好懂

本文永久链接

如果有疑问评论最好在上面链接到评论区里评论

C++ 中文周刊 第117期

09 Jun 14:12
07efd0b
Compare
Choose a tag to compare

周刊项目地址

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

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

提交 issue

感谢不语 赞助


资讯

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

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

llvm weekly我可能也看一下,顺便展开讲讲。可能更新以后要挪到周六,周知一下

看内容多不多,不多就讲讲llvm weekly

另外视频压了很多没看。看视频最大的问题是关键信息提取太慢了,lighting talk能快一些,长视频突出一个铺垫拉满。

这周末我准备把2021的 lighting talk看一遍,周末可能发一下整理,或者下周发一下整理

文章

本周最热闹的应该是这个问题 DeepMind AI 创造出比人类快 70% 的排序算法,会产生哪些影响?

实际上根据他的压测结果,没有70%这个结论。有点收益,但不是70%

代码去年就合入了

review链接 https://reviews.llvm.org/D118029

代码链接 llvm/llvm-project@194d196

这个问题下大家在互相拉扯,我之前看到这个也是先入为主嘲讽一波,啊原来汇编17行,你优化了一下,16行了,AI可以了,收收味

不能说没进步,首先不是introspect sort改成pdqsort这种跨步收益(llvm已经是pdqsort了。gcc不是,想用可以用boost)

其次看benchmark #53

有些场景甚至慢了。但前排都是提升的,基本%1。

不过其他答案给了个AI优化hash算法的。这个确实牛逼 abseil/abseil-cpp@74eee2a

我一直觉得hash算法这玩意不是人想的,AI感觉在这个方向能有更大收获

依赖输入的AI优化说实话有点像PGO

优化尚不明显,AI仍需努力

话说问问ChatGPT老师也能得到相同的结论 https://zhuanlan.zhihu.com/p/635847068

之前聊过这个http://0x80.pl/notesen/2023-04-09-faster-parse-ipv4.html

Daniel Lemire他也感兴趣,毕竟他写了个url解析库ada 仓库 https://github.com/ada-url/ada

需要这玩意。他根据上面这哥们的代码改了一版本,相比gcc,性能提升十倍

代码

https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2023/06/08/src/sse_inet_aton.c

另外,文中提到的 simdzone 是dns zone文件解析库 代码 https://github.com/NLnetLabs/simdzone

说实话我一直没研究SIMD。感觉躲不开了。这里标记个TODO。早晚得研究下

一个很巧妙的办法让成员避免比较

比如一个类

struct A {
    using allocator_type = std::pmr::polymorphic_allocator<std::byte>;
    int data1;
    int data2;
    int data3;
    allocator_type alloc;
    A(int a, int b, int c, allocator_type d) : data1(a), data2(b), data3(c), alloc(d) {}
    friend auto operator<=>(const A&, const A&) = default;
};

怎么实现<=> 能不比较alloc呢?

像咱这种笨比,就手写,不default,写成里面的来比较

比如

friend std::strong_ordering operator<=>(const A& lhs, const A& rhs) {
  if (auto r = (lhs.data1 <=> rhs.data1); r != 0) {
    return r;
  } else if (auto r = (lhs.data2 <=> rhs.data2); r != 0) {
    return r;
  } else if (auto r = (lhs.data3 <=> rhs.data3); r != 0) {
    return r;
  } else {
    return (1 <=> 1);
  }
}

或者聪明一点,使用tie

friend std::strong_ordering operator<=>(const A& lhs, const A& rhs) {
  auto tie = [](auto& x) { return std::tie(x.data1, x.data2, x.data3); };
  return tie(lhs) <=> tie(rhs);
}
friend bool operator==(const A& lhs, const A& rhs) {
  auto tie = [](auto& x) { return std::tie(x.data1, x.data2, x.data3); };
  return tie(lhs) == tie(rhs);
}

但还是要手写,能不能default?

作者的办法

#include <compare>
#include <memory_resource>
#include <cassert>

struct ComparisonIgnorerBase {
    using CIB = ComparisonIgnorerBase;
    constexpr friend auto operator<=>(const CIB&, const CIB&) = default;
};

template<class T>
struct ComparisonIgnorer : ComparisonIgnorerBase {
    T t_;
    ComparisonIgnorer(T t) : t_(std::move(t)) {}
};

struct A {
    using allocator_type = std::pmr::polymorphic_allocator<std::byte>;
    int data1;
    int data2;
    int data3;
    ComparisonIgnorer<allocator_type> alloc;
    A(int a, int b, int c, allocator_type d) : data1(a), data2(b), data3(c), alloc(d) {}
    friend auto operator<=>(const A&, const A&) = default;
};

int main() {
    std::pmr::monotonic_buffer_resource mr1;
    std::pmr::monotonic_buffer_resource mr2;
    A a = {1,2,3, &mr1};
    A b = {1,2,3, &mr2};
    A c = {3,1,2, &mr1};
    assert(a == b);
    assert(a < c);
}

确实挺巧妙,不需要比较的成员,替他实现一个空的 <=> 鸠占鹊巢

看<程序员自我修养 链接装载库> 就行了。这个文章也是讲的那玩意

#include <span>

constexpr std::array a = {1, 2, 3, 4, 5};
constexpr std::span s{a};

static_assert(s[0]==a[0]);

看不懂

可读性问题,不是必要的operator() 不要用

struct StorageLoader
{
    template<typename DataType>
    DataType Load(StorageOptions<DataType> const* options);

    template<typename DataType>
    DataType operator()(StorageOptions<DataType> const* options)
    { return Load(options); }
};


// 1 Using function call operator
data1 = storageLoader(&data1Options);

// 2 Using named method
data1 = storageLoader.Load(&data1Options);

// 3 Named method works better for nullptr
data1 = storageLoader.Load<Data1>(nullptr);

第一种显然是不必要的

不必要的operator ()实现这玩意整花活没意义

  • 偶然看到的一段代码
repair_status repair::task_manager_module::get(int id) const {
    if (std::cmp_greater(id, _sequence_number)) {
        throw std::runtime_error(format("unknown repair id {}", id));
    }
    auto it = _status.find(id);
    if (it == _status.end()) {
        return repair_status::SUCCESSFUL;
    } else {
        return it->second;
    }
}

std::cmp_greater 是c++20的,之前介绍过,语义是

-1 > 0u; // true
std::cmp_greater(-1, 0u); // false

跟着最新标准演进代码,收益还是非常明显的,起码不会写错

浮点数的精度问题

视频

讲代码怎么写的,讲的挺好的 PPT在这里

https://github.com/mattgodbolt/correct-by-construction

感觉值得用中文讲一遍

想要演讲却不知道说啥?没有自信?这里有一堆指导视频教你做演讲

  • Tina Ulbrich - But I have nothing to talk about! 看标题就绷不住了。简单说就是根据经验整理观点
  • Inbal Levi - Distilling your message
  • Hendrik Niemeyer - Doing research on your talks with Zettelkasten (and some tools)
  • Andrei Alexandrescu - Stop working on your slides AA说的是演讲主要是传递观点传递信息,只要不是错的,不需要你懂很多,20%就够,传播思想。这点我很赞同,本周报也是这样的,不保证结论正确,只要基本正确,传播观点就行
  • Jens Weller - Presenting Code
  • Clare Macrae - Better Code Samples in Programming Talks
  • Chandler Carruth - About giving live demos 这个哥们经常做live demo演讲,我还记得他讲LLVM那个。挺厉害的。他说的主要就是练习,他这个小演讲就是手敲命令演示的
  • Patricia Aas - Telling a story
  • Kate Gregory - How to end a talk

Andrei Alexandrescu的和Chandler Carruth 的演讲总是有意思,他们的演讲我一直都看。其他人说的哎也就那么回事

这个讲的是之前clang那个死循坏优化代码,UB介绍。已经介绍一万多遍了

介绍view的使用和一些bug,感觉之前说过类似的案例,比如range loop bug 迭代器边界问题等等。这个值得看一下,感觉今年cppcon还会讲一遍

开源项目需要人手

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

新项目介绍/版本更新


本文永久链接

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

C++ 中文周刊 第116期

05 Jun 09:52
88aa533
Compare
Choose a tag to compare

周刊项目地址

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

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

提交 issue

感谢不语 chenbh 赞助

上周团建耽搁。


资讯

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

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

ACCU会议开始,后续会把有意思的看一下

文章

#include <array>

template<auto N> constexpr auto foo() { return N; }

template<auto N = 42>
constexpr auto dispatch(auto n) -> int{
    using foo_return_type = std::invoke_result<decltype(&foo<0>)>::type;
    const auto jump_table = []<auto ...I>(std::index_sequence<I...>){
        return std::array<foo_return_type, sizeof...(I)>{
            (foo<I>())...
        };
    }(std::make_index_sequence<N>{});
    return jump_table[n];
};

static_assert(1 == dispatch(1));
static_assert(7 == dispatch(7));
static_assert(23 == dispatch(23));

感觉没啥用

想一个函数处理所有容器类,比如vector

template<typename...Args>
void accept_any_vector(std::vector<Args...> v) {
    using vector = std::vector<Args...>;
    using Value = typename vector::value_type;
    using Allocator = typename vector::allocator_type;

    ...
}

没啥用

只有完美转发才用std::forward

直接看代码吧,optional的辅助函数,c++23可用,说了很多次了,早就该加了

auto p = make_pizza(pizza_size::regular)
   .or_else([]() -> std::optional<pizza> {
      std::cout << "Failed to create pizza\n"; 
      return std::nullopt; })
   .and_then(add_pepperoni)
   .or_else([]() -> std::optional<pizza> {
      std::cout << "Failed to add pepperoni\n"; 
      return std::nullopt; })
   .and_then(add_basil)
   .or_else([]() -> std::optional<pizza> {
      std::cout << "Failed to add basil\n"; 
      return std::nullopt; })
   .and_then(add_artichokes)
   .or_else([]() -> std::optional<pizza> {
      std::cout << "Failed to add pepperoni\n"; 
      return std::nullopt; })
   .transform(get_price);

优化bloom filter提升性能,代码在这里https://github.com/FastFilter/fastfilter_cpp

原理我没有看懂,这里标记一个TODO

Raymond Chen讲的这种需求我没有看懂

看不懂了

std::exchange std::apply也noexcept了。没啥说的

视频

array 无法move,可以掏空容器内的成员

最近看了cppcon 2022的lighting talk,有几个挺有意思。想看的可以b站搜一下。

或者复制这个链接 https://www.bilibili.com/video/BV17X4y117Hx

或者看油管 https://www.youtube.com/playlist?list=PLHTh1InhhwT6U_8ehqxpB7-O1KF_5WwC4

下面是总结,链接用的油管的

他这个点子有意思,是利用clangd 分析可以move的代码,集成到CI。没开源

这个是拉人做开源项目 地址https://github.com/SFML/SFML

这个是要求debug版本性能别太差 move forward addressof 在debug版本下根本没生效,没发挥作用。只能自己hook。目前编译器也在演化这里

使用前要测量一下,或者对业务熟悉,知道这里满足用unlikely。甚至PGO都不能保证?

压测发现直接调用rdtsc和调用系统的clock_gettime没差多少。实际上系统clock_gettime已经用rdtsc实现了。需要确认一下系统时钟源看一下是不是tsc。一些旧博客需要更新了

reveal.js PPT 直接调用godbolt 创意挺有趣

介绍的有点粗糙 std::start_lifitime_as

炫技,代码在这里 https://github.com/boost-ext/mp

开源项目需要人手

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

本文永久链接

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