公众号
qq群 点击进入
RSS https://github.com/wanghenshui/cppweeklynews/releases.atom
欢迎投稿,推荐或自荐文章/软件/资源等
请提交 issue 或评论区留言
本期文章由 不语 沧海 彩虹蛇皮虾 赞助
jetbrain发布了23年 c++ 生态回顾 https://blog.jetbrains.com/clion/2024/01/the-cpp-ecosystem-in-2023/
感兴趣的可以看看,没啥意思
标准委员会动态/ide/编译器信息放在这里
一月邮件
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/#mailing2024-01
其实这个概念之前讨论了很久,老熟人Arthur O’Dwyer 提了很多相关的提案 patch。大家感兴趣的可以读一下。算是一个优化的点
之前也提到过,比如
讲trivial relocation的现状以及开源实现
T.r. types | Non-t.r. types | Throwing-move types | Rightward motion (`insert`) | Leftward motion (`erase`) | Non-pointer iterators | ||
STL Classic (non-relocating) | std::copy | N/A | N/A | ✓ | UB | ✓ | ✓ |
std::copy_n | N/A | N/A | ✓ | UB | UB | ✓ | |
std::copy_backward | N/A | N/A | ✓ | ✓ | UB | ✓ | |
cstring | memcpy | ✓ | UB | ✓ | UB | UB | SFINAE |
memmove | ✓ | UB | ✓ | ✓ | ✓ | SFINAE | |
Qt | q_uninitialized_relocate_n | ✓ | ✓ | ✓? | UB | UB | SFINAE |
q_relocate_overlap_n | ✓ | ✓ | ✓ | ✓ | ✓ | SFINAE | |
BSL | destructiveMove | ✓ | ✓ | ✓ | UB | UB | SFINAE |
P2786R0 | trivially_relocate | ✓ | SFINAE | SFINAE | ✓ | ✓ | SFINAE |
relocate | ✓ | ✓ | SFINAE | ✓ | ✓ | SFINAE | |
move_and_destroy | ✓ | ✓ | SFINAE | UB | ? | ✓ | |
P1144R6 | uninitialized_relocate | ✓ | ✓ | ✓ | UB | ✓ | ✓ |
uninitialized_relocate_n | ✓ | ✓ | ✓ | UB | ✓ | ✓ | |
P1144R7 | uninitialized_relocate_backward | ✓ | ✓ | ✓ | ✓ | UB | ✓ |
还给人folly提MR https://github.com/facebook/folly/pull/1934
等等,周边信息很多
直接贴代码 https://godbolt.org/z/rq9dsGxh5
#include <algorithm>
double incorrect_clamp(double v, double lo, double hi){
return std::min(hi, std::max(lo, v));
}
double official_clamp(double v, double lo, double hi){
return std::clamp(v, lo, hi);
}
double official_clamp_reordered(double hi, double lo, double v){
return std::clamp(v, lo, hi);
}
double correct_clamp(double v, double lo, double hi){
return std::max(std::min(v, hi), lo);
}
double correct_clamp_reordered(double lo, double hi, double v){
return std::max(std::min(v, hi), lo);
}
对应的汇编
incorrect_clamp(double, double, double):
maxsd xmm0, xmm1
minsd xmm0, xmm2
ret
official_clamp(double, double, double):
maxsd xmm1, xmm0
minsd xmm2, xmm1
movapd xmm0, xmm2
ret
official_clamp_reordered(double, double, double):
maxsd xmm1, xmm2
minsd xmm0, xmm1
ret
correct_clamp(double, double, double):
minsd xmm2, xmm0
maxsd xmm1, xmm2
movapd xmm0, xmm1
ret
correct_clamp_reordered(double, double, double):
minsd xmm1, xmm2
maxsd xmm0, xmm1
ret
为什么正确的代码多了一条 mov xmm?
浮点数 +-0的问题,标准要求返回第一个参数,比如 std::clamp(-0.0f, +0.0f, +0.0f)
如果配置了-ffinite-math-only -fno-signed-zeros
最终汇编是一样的 https://godbolt.org/z/esMY18a5z
举一个fuzz例子,大家都学一下
你看这个接口感觉可能无从下手
extern "C"
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
我们要测试的接口长这样
template <typename T, size_t Capacity>
requires (std::is_nothrow_move_constructible_v<T> && std::is_nothrow_move_assignable_v<T>)
class fixed_stack
{
public:
T& push(T t) {
if (size() == capacity()) throw size_error("push on full stack");
return data_[++size_] = std::move(t);
}
T& back() {
if (empty()) throw size_error("back on empty stack");
return data_[size_];
}
T pop() {
if (empty()) throw size_error("pop on empty stack");
return std::move(data_[size_--]);
}
[[nodiscard]] bool empty() const { return size() == 0; }
[[nodiscard]] size_t size() const { return size_; }
[[nodiscard]] static size_t capacity() { return Capacity; }
private:
size_t size_ = 0;
std::array<T, Capacity> data_{};
};
考虑一下测试代码
可能长这样
truct failure : std::string {
using std::string::string;
};
#define REQUIRE(...) if (__VA_ARGS__) {;} else throw failure #__VA_ARGS__)
#define FAIL(...) throw failure(__VA_ARGS__)
int main() {
unsigned fail_count = 0;
struct test {
const char* name;
std::function<void()> f;
};
test tests[] {
{ "default constructed stack is empty",
[]{
fixed_stack<int, 8> s;
REQUIRE(s.size() == 0);
REQUIRE(s.empty());
}},
{ "Each push grows size by one",
[] {
fixed_stack<int, 8> s;
s.push(3);
REQUIRE(s.size() == 1);
s.push(2);
REQUIRE(s.size() == 2);
s.push(8);
REQUIRE(s.size() == 3);
}
},
{ "Pop returns the pushed elements in reverse order",
[]{
fixed_stack<int, 8> s;
s.push(3);
s.push(2);
s.push(8);
REQUIRE(s.pop() == 8);
REQUIRE(s.pop() == 2);
REQUIRE(s.pop() == 3);
}
},
{ "pop on empty throws",
[]{
fixed_stack<int, 8> s;
s.push(3);
s.pop();
try {
s.pop();
FAIL("didn't throw");
}
catch (const size_error&)
{
// good!
}
}}
};
for (auto& t : tests){
try {
std::cout << std::setw(60) << std::left << t.name << "\t";
t.f();
std::cout << "PASS!";
} catch (const failure& f) {
std::cout << "FAILED!\nError: " << f << '\n';
++fail_count;
} catch (...) {
std::cout << "FAILED!\nUnknown reason!";
++fail_count;
}
std::cout << '\n';
}
}
现在咱们考虑怎么把这个测试代码改写成fuzz test?
简单来说输入的就是一段二进制,怎么根据这个二进制拆解出不同的动作,拆解出不同的输入?
struct exhausted {};
struct source {
std::span<const uint8_t> input;
template <typename T>
requires (std::is_trivial_v<T>)
T get() {
constexpr auto data_size = sizeof(T);
if (input.size() < data_size) throw exhausted{};
alignas (T) uint8_t buff[data_size];
std::copy_n(input.begin(), data_size, buff);
input = input.subspan(data_size);
return std::bit_cast<T>(buff);
}
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
source s;
std::vector<int> comparison;
std::optional<fixed_stack<std::unique_ptr<int>, 8>> stack;
try {
for (;;) {
if (!stack.has_value()) {
stack.emplace();
}
// 通过source 拿一个u8来枚举动作
const auto action = s.get<uint8_t>();
switch (action) {
case 0: // push
{
// 通过source拿到需要的输入数据
const int v = s.get<int>();
const auto size = stack->size();
try {
stack->push(std::make_unique<int>(v));
comparison.push_back(v);
assert(stack->size() == comparison.size());
assert(stack->back() != nullptr);
assert(*stack->back() == v);
} catch (size_error) {
assert(size == stack->capacity());
assert(stack->size() == size);
}
}
break;
case 1: // pop
{
if (!stack->empty()) {
auto v = stack->pop();
assert(v);
assert(*v == comparison.back());
comparison.pop_back();
}
}
break;
case 2: // destroy
{
stack.reset();
comparison = {};
}
}
}
} catch (exhausted) { /* done */ }
return 0; // Values other than 0 and -1 are reserved for future use.
}
编译带上-fsanitize=address,undefined,fuzzer --coverage
能抓到错误,抓到错误调试就是另一个流程了,gdb挂上去调就行了。留做作业吧
学吧,学无止境
主要是控制块的管理
这个哥们把一个工具优化到能用的水平,并介绍了相关设计
这个精力投入令人佩服,代码在这里 https://github.com/qqiangwu/cppsafe
感兴趣的可以体验一下
介绍static key的
130期 咱们提到过Bounded dynamicism with cross-modifying code
也是类似的玩意,也有现成的库提供,https://github.com/backtrace-labs/dynamic_flag
不明觉厉
利用statufull template来检查,算了吧,不看了
其实就是record录制
可能的一种用法
#ifdef NDEBUG
#define assert(x) [[assume(x)]]
#else
#define assert(x) /* unreach, abort都行,自定义 */
#endif
感觉不如builtin_expect,暂时别用
讲ecs框架和entt的
讲entt的视频,最近我看到一个不错的 b站 BV1X841127Rq
讲value的。听困了
boost 新的scope库被合入 https://lists.boost.org/Archives/boost/2024/01/255717.php
seastar 最近改动较少
主要是代码规范化 比如 https://github.com/scylladb/seastar/pull/2054
@@ -224,11 +225,7 @@ template <typename T, size_t Capacity>
inline
circular_buffer_fixed_capacity<T, Capacity>::circular_buffer_fixed_capacity(circular_buffer_fixed_capacity&& x) noexcept
: _begin(x._begin), _end(x._end) {
- // This is std::uninitialized_move, but that is c++17 only
- auto dest = begin();
- for (auto& obj : x) {
- new (&*dest++) T(std::move(obj));
- }
+ std::uninitialized_move(x.begin(), x.end(), begin());
}
另外就是修复bug,延长请求,请求没结束不释放handler https://github.com/scylladb/seastar/pull/2044/
folly类似seastar 把版本切17之后做了很多的适配和bugfix
比较好玩的是folly::tape 类似vector<vector>
但性能更好,常见场景就是 vector<vector<char>>
https://github.com/facebook/folly/pull/2109/
在使用场景上做了取舍
实现思路就是拍扁,一维,比如tape<vector<char>>
就是vector<char>
, 记录所有元素的index和offset
没有SSO优化的话,这种形态比vector<vector<char>>
局部性要好
感兴趣大家可以以及看一看
改动非常多,一月改动如下
还是有很多可以学习的地方都
另外rocksdb我也会更新,这个更新的内容不会放在这里,会单独发
也会放在 https://wanghenshui.github.io/rocksdb-doc-cn 里
寒冬了兄弟们。最近在群里聊天探讨活路,感叹数据库行业之难活,dead business。
贴近业务才能活,但业务有时候都活不下去。大家都在瞎几把忙
另外就是知识的诅咒吧,沟通尽量实事求是 有可能信息不对等大家不同
所以说读者读到不懂的地方,一定要评论,喷我,不对话交换共识,没有进步
另外最近的每日一题有点难,啥也不是,散会!