公众号

点击「查看原文」跳转到 GitHub 上对应文件,链接就可以点击了
qq群 点击进入 满了加这个 729240657
欢迎投稿,推荐或自荐文章/软件/资源等,评论区留言
本文感谢选择公理赞助,祝您身体健康万事如意
标准委员会动态/ide/编译器信息放在这里
编译器信息最新动态推荐关注hellogcc公众号 本周更新 2025-01-08 第288期
作者讲的是属性的可忽略性质导致和关键字不协调,比如[[deprecated]], [[nodiscard]] 为什么不是关键字,但constinit是
又比如override可以忽略,但他不是属性,是关键字
c++26引入了trivially_relocatable_if_eligible和replaceable_if_eligible两个关键字,按理说他应该类似属性这种可忽略,保持向后兼容,但不是
属性就不应该可忽略,这样能让这些奇怪关键字回归属性来用
大家有什么见解?我觉得确实有点乱,对付用的水平,什么属性,我直接一个宏代替
使用有符号类型来比较无符号数
考虑溢出加法
如果将最小可能值(非常小的负数值)M与有符号整数x和y相加,则(x + M) < (y + M)等同于将x和y视为无符号整数值进行比较。
原理:
将0映射到M(0成为最小值), 将所有正值映射到范围从M+1到-1(正值变为负值), 负值都会溢出,使最后一位变为零,而其他位保持不变,从而将负值从M到-1映射到范围0到-M-1(负值变为正值)
考虑避免c++溢出问题,使用异或,因为 x + M = (x^M) + ((x&M)«1) 后半部分无符号数就是0,所以直接比较异或 (x ^ M) < (y ^ M)
主要讲的mmap/xmalloc/realloc/VirtualAlloc延长空间分配行为。没啥值得看的
挺有意思,我直接贴代码了 https://godbolt.org/z/44f9eM41W
#include <vector>
#include <string>
#include <iostream>
enum class _tree_return {
    Continue = 0,
    Prune,
    Break
};
template<typename T, typename F1, typename F2, typename F3>
_tree_return _for_tree(T initial, F1 condition, F2 branch, F3 visit) {
    _tree_return result = visit(initial);
    if(result == _tree_return::Break) return _tree_return::Break;
    if(result != _tree_return::Prune) {
        for(T subnode : branch(initial)) {
            if(condition(subnode)) {
                _tree_return result = _for_tree(subnode, condition, branch, visit);
                if(result == _tree_return::Break) return _tree_return::Break;
            }
        }
    }
    return _tree_return::Continue;
}
#define tree_break return _tree_return::Break
#define tree_prune return _tree_return::Prune
#define tree_continue return _tree_return::Continue
                                                          //v-- semicolon to not allow you to get the return value here
#define for_tree(XName, Xinitial, Condition, Branch, Visit) ;_for_tree(Xinitial, \
[&](decltype(Xinitial) XName){ return Condition; }, \
[&](decltype(Xinitial) XName){ return std::vector<decltype(Xinitial)>Branch; }, \
[&](decltype(Xinitial) XName){ Visit; return _tree_return::Continue; })
//excuse the use of a std::vector in there, I guess you cant return an initialize_list from a lambda
//that wouldn't really be an issue if this was implemented at the language level instead of hacked together from lambdas and macros
struct Node {
    Node* left = NULL;
    Node* right = NULL;
    std::string value;
    Node(std::string value):value(value){}
};
int main() {
    //syntax is a little uglier than it could be if it was native
    
    //imperative tree sample
    for_tree(x, std::string(""), x.size()<=3, ({x+"a", x+"b", x+"c"}), {
        std::cout << x << std::endl;
    });
    
    //tree structure sample
    Node mytree("root");
    mytree.left = new Node("left");
    mytree.right = new Node("right");
    mytree.left->left = new Node("leftleft");
    
    for_tree(x, &mytree, x != NULL, ({x->left, x->right}), {
        std::cout << x->value << std::endl;
    });
    return 0;
}
struct ConfigValues {
  uint32_t                  chksum;
  std::array<uint32_t, 128> values;
};
bool ProcessData(std::span<unsigned char> bytes)
{
  if(bytes.size() < sizeof(ConfigValues)) { return false; }
  //UB 类型双关
  ConfigValues* cfgValues = reinterpret_cast<ConfigValues*>(bytes.data());
  return HandleConfigValues(cfgValues);
}
用bit_cast复制一份可能开销太大,怎么办?
struct ConfigValues {
  uint32_t                  chksum;
  std::array<uint32_t, 128> values;
};
bool ProcessData(std::span<unsigned char> bytes)
{
  if(bytes.size() < sizeof(ConfigValues)) { return false; }
  A Using std::start_lifetime_as
  ConfigValues* cfgValues = std::start_lifetime_as<ConfigValues>(bytes.data());
  return HandleConfigValues(cfgValues);
}
注释错误更好看,模版报错格式化,输出报告SARIF更文档化,可折叠, 默认-std=gnu23,以及提供gdiagnostic api(c,py)方便分析报告
总算现代了点
使用SWAR高效检测控制字符(小于32)、引号和反斜杠
直接贴代码,看变量名字就能懂
bool has_json_escapable_byte(uint64_t x) {
    uint64_t is_ascii = 0x8080808080808080ULL & ~x;
    uint64_t lt32 =
        (x - 0x2020202020202020ULL);
    uint64_t sub34 = x ^ 0x2222222222222222ULL;
    uint64_t eq34 = (sub34 - 0x0101010101010101ULL);
    uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL;
    uint64_t eq92 = (sub92 - 0x0101010101010101ULL);
    return ((lt32 | eq34 | eq92) & is_ascii) != 0;
}
优化版本
bool has_json_escapable_byte(uint64_t x) {
    uint64_t is_ascii = 0x8080808080808080ULL & ~x;
    uint64_t xor2 = x ^ 0x0202020202020202ULL;
    uint64_t lt32_or_eq34 = xor2 – 0x2121212121212121ULL;
    uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL;
    uint64_t eq92 = (sub92 - 0x0101010101010101ULL);
    return ((lt32_or_eq34 | eq92) & is_ascii) != 0;
}
合并了32/34计算
场景是你想掏空optional
template<typename T>
T do_something() {
    std::optional<T> opt = some_oracle<T>();
    if (!opt) {
        std::cerr << "Something terrible happened\n";
        std::exit(EXIT_FAILURE);
    }
    return *opt; // equivalent to .value(), but doesn't throw an exception
}
这样写会复制,这也是常见的用optional错误
比如大家会这样写
std::optional<T> opt = some_good_oracle<T>(); /* assume opt.has_value() */
if (opt.has_value())
    f1( std::move(opt.value()) ); // move the value to avoid copying
                                  // from here onwards opt doesn't have a value
if (opt.has_value())  // true, unexpected!
    f2( std::move(opt.value()) ); // move again!
                                  // in reality f2 got an empty/garbage T
容易用错,我们需要掏空optional
// Bad
// auto x = std::move(opt.value());
// Good
auto x = std::move(opt).value();
接着上一个,move这些类型,非常容易用错, 代码例子如下
#include <format>
#include <exception>
#include <variant>
#include <iostream>
class LifeTime {
	int a_ = 0;
	public:
    LifeTime(int a):a_(a){
        std::cout << "LifeTime(): " << a_ << std::endl;
    }
    ~LifeTime() {
        std::cout << "~LifeTime(): " << a_ << std::endl;
    }
	LifeTime(const LifeTime&) {
        std::cout << "LifeTime(const LifeTime&): " << a_ << std::endl;
    }
	LifeTime(LifeTime&& other) noexcept {
		this->a_ = other.a_;
        std::cout << "LifeTime(LifeTime&&): " << a_ << std::endl;
    }
	LifeTime& operator=(const LifeTime&) {
        std::cout << "LifeTime& operator=(const LifeTime&): " << a_ << std::endl;
        return *this;
    }
	LifeTime& operator=(LifeTime&&) noexcept {
        std::cout << "LifeTime& operator=(LifeTime&&): " << a_ << std::endl;
        return *this;
    }
};
//break RVO
std::optional<LifeTime> create_v1() {
	LifeTime a{ 1 };
	return a;
}
//break RVO
std::optional<LifeTime> create_v2() {
	return LifeTime{2};
}
//break RVO
std::optional<LifeTime> create_v3() {
	std::optional<LifeTime> opt;
	opt = LifeTime{ 3 };
	return opt;
}
std::optional<LifeTime> create_v4() {
	std::optional<LifeTime> opt;
	opt.emplace(4);
	return opt;
}
std::optional<LifeTime> create_v5() {
	return std::optional<LifeTime>{5};
}
std::optional<LifeTime> create_v6() {
	return std::optional<LifeTime>{std::in_place, 6};
}
说大部分依靠UB的优化都可以通过编译器优化/LTO捡回来。只能说仁者见仁吧。
llvm ir级别重写dynamiccast带来收益,效果堪比dyn_cast。但使用条件苛刻,继承必须树形结构
核心思想就是既然是树形结构那就拍扁到一个vtable上,大家用地址+offset比较,跳过typeinfo比较
看一乐,有点思路借鉴
数组结构体怎么改成结构体数组,利用tuple,我照着他的思路抄了一个,很有意思 https://godbolt.org/z/8PGP3n58q
#include <vector>
template <size_t...>
struct IndexSequence
{
};
//#if __has_builtin(__type_pack_element)
////////////////////////////////////////////////////////////
#define SFML_BASE_TYPE_PACK_ELEMENT(N, ...) __type_pack_element<N, __VA_ARGS__>
//#if __has_builtin(__integer_pack)
////////////////////////////////////////////////////////////
#define SFML_BASE_MAKE_INDEX_SEQUENCE(N) IndexSequence<__integer_pack(N)...>
#define SFML_BASE_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
#define SFML_BASE_REMOVE_REFERENCE(...) __remove_reference(__VA_ARGS__)
#define SFML_BASE_MOVE(...) static_cast<SFML_BASE_REMOVE_REFERENCE(decltype(__VA_ARGS__)) &&>(__VA_ARGS__)
////////////////////////////////////////////////////////////
template <size_t I, typename T>
struct SoABase
{
    ////////////////////////////////////////////////////////////
    enum : size_t
    {
        index = I
    };
    ////////////////////////////////////////////////////////////
    [[no_unique_address]] std::vector<T> data;
};
////////////////////////////////////////////////////////////
template <typename, typename...>
class SoA;
////////////////////////////////////////////////////////////
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define SOA_AS_BASE(I)       static_cast<SoABase<I, SFML_BASE_TYPE_PACK_ELEMENT(I, Ts...)>&>(*this)
#define SOA_AS_CONST_BASE(I) static_cast<const SoABase<I, SFML_BASE_TYPE_PACK_ELEMENT(I, Ts...)>&>(*this)
#define SOA_ALL_BASES()      static_cast<SoABase<Is, Ts>&>(*this)
////////////////////////////////////////////////////////////
template <size_t... Is, typename... Ts>
class SoA<IndexSequence<Is...>, Ts...> : private SoABase<Is, Ts>...
{
public:
    ////////////////////////////////////////////////////////////
    [[gnu::always_inline]] void clear()
    {
        (SOA_ALL_BASES().data.clear(), ...);
    }
    ////////////////////////////////////////////////////////////
    [[gnu::always_inline]] void reserve(size_t capacity)
    {
        (SOA_ALL_BASES().data.reserve(capacity), ...);
    }
    ////////////////////////////////////////////////////////////
    [[gnu::always_inline]] void resize(size_t size)
    {
        (SOA_ALL_BASES().data.resize(size), ...);
    }
    ////////////////////////////////////////////////////////////
    [[gnu::always_inline]] void push_back(auto&&... values)
    {
        (SOA_ALL_BASES().data.push_back(SFML_BASE_FORWARD(values)), ...);
    }
    ////////////////////////////////////////////////////////////
    [[nodiscard, gnu::always_inline, gnu::pure]] size_t getSize() const
    {
        return SOA_AS_CONST_BASE(0).data.size();
    }
    ////////////////////////////////////////////////////////////
    template <size_t... Js>
    [[gnu::always_inline]] void withNth(size_t i, auto&& f)
    {
        f(SOA_AS_BASE(Js).data[i]...);
    }
    ////////////////////////////////////////////////////////////
    [[gnu::always_inline]] void withAllNth(size_t i, auto&& f)
    {
        f(SOA_ALL_BASES().data[i]...);
    }
    ////////////////////////////////////////////////////////////
    template <size_t... Js>
    [[gnu::always_inline]] void withSubRange(size_t start, size_t end, auto&& f)
    {
        for (size_t i = start; i < end; ++i)
            f(SOA_AS_BASE(Js).data[i]...);
    }
    ////////////////////////////////////////////////////////////
    [[gnu::always_inline]] void withAllSubRange(size_t start, size_t end, auto&& f)
    {
        for (size_t i = start; i < end; ++i)
            f(SOA_ALL_BASES().data[i]...);
    }
    ////////////////////////////////////////////////////////////
    template <size_t... Js>
    [[gnu::always_inline]] void with(auto&& f)
    {
        for (size_t i = 0u; i < getSize(); ++i)
            f(SOA_AS_BASE(Js).data[i]...);
    }
    ////////////////////////////////////////////////////////////
    [[gnu::always_inline]] void withAll(auto&& f)
    {
        for (size_t i = 0u; i < getSize(); ++i)
            f(SOA_ALL_BASES().data[i]...);
    }
    ////////////////////////////////////////////////////////////
    template <size_t... Js>
    void eraseIfByShifting(auto&& f)
    {
        const size_t n = getSize();
        // Find the first element to remove.
        size_t i = 0;
        while (i < n && !f(SOA_AS_BASE(Js).data[i]...))
            ++i;
        // For the remaining elements, shift over those that must be kept.
        size_t newSize = i;
        for (; i < n; ++i)
        {
            if (f(SOA_AS_BASE(Js).data[i]...))
                continue;
            if (newSize != i)
                ((SOA_ALL_BASES().data[newSize] = SFML_BASE_MOVE(SOA_ALL_BASES().data[i])), ...);
            ++newSize;
        }
        // Resize all columns to the new size.
        ((SOA_ALL_BASES().data.resize(newSize)), ...);
    }
    ////////////////////////////////////////////////////////////
    template <size_t... Js>
    void eraseIfBySwapping(auto&& f)
    {
        size_t n = getSize();
        size_t i = 0;
        // Process elements, swapping out removed ones.
        while (i < n)
        {
            if (!f(static_cast<SoABase<Js, SFML_BASE_TYPE_PACK_ELEMENT(Js, Ts...)>&>(*this).data[i]...))
            {
                ++i;
                continue;
            }
            // Swap the current element with the last one, then reduce the container size.
            --n;
            ((std::swap(SOA_ALL_BASES().data[i], SOA_ALL_BASES().data[n])), ...);
            // Do not increment i; check the new element at i.
        }
        // Resize all columns to the new size.
        ((SOA_ALL_BASES().data.resize(n)), ...);
    }
};
////////////////////////////////////////////////////////////
#undef SOA_ALL_BASES
#undef SOA_AS_CONST_BASE
#undef SOA_AS_BASE
////////////////////////////////////////////////////////////
template <typename... Ts>
using SoAFor = SoA<SFML_BASE_MAKE_INDEX_SEQUENCE(sizeof...(Ts)), Ts...>;
#include <string>
#include <iostream>
int main() {
    SoAFor<float,  int, std::string> dummy;
    dummy.push_back(1.1, 2, "33");
    dummy.with<0, 1>(
    [](float& vel, int& acc) { vel += acc; });
    dummy.withAll(
    [](float v, int& a, std::string& s) { std::cout << v << a << s; });
}
问题:实现一个TwoDIterator 代码脚手架
int main() {
    std::vector<std::vector<int>> input = {
        {1, 2}, {3}, {}, {4, 5, 6}};
    TwoDIterator it(input);
    while (it.has_next())
        std::print("{} ", it.next());
    // 1, 2, 3, 4, 5, 6
}
range大神这时候就说了,一行秒了
auto flat = nested | std::views::join;
常规解法就是先判断每行结束没,每行结束了就整体移动一下,两个index
#include <vector>
#include <stdexcept>
#include <print>
class TwoDIterator {
public:
    using Vec2D = std::vector<std::vector<int>>;
    TwoDIterator(const Vec2D& data) 
    : data_(data), outer_(0), inner_(0) {
        advance_to_next_valid();
    }
    bool has_next() const {
        return outer_ < data_.size();
    }
    int next() {
        if (!has_next()) throw std::out_of_range("No more elements");
        int val = data_[outer_][inner_];
        ++inner_;
        advance_to_next_valid();
        return val;
    }
private:
    void advance_to_next_valid() {
        while (outer_ < data_.size() && inner_ >= data_[outer_].size()) {
            ++outer_;
            inner_ = 0;
        }
    }
    const Vec2D& data_;
    size_t outer_, inner_;
};
怎么组合一下range?
很巧,join_view是有iter的
#include <vector>
#include <stdexcept>
#include <print>
#include <ranges>
class TwoDIteratorJoin {  
public:  
    using Vec2D = std::vector<std::vector<int>>;  
    using JoinView = std::ranges::join_view<std::ranges::ref_view<const Vec2D>>;  
    using Iterator = std::ranges::iterator_t<JoinView>;  
  
    explicit TwoDIteratorJoin(const Vec2D& data)  
        : flattened_(data | std::views::join),  
          iter_(flattened_.begin()),  
          end_(flattened_.end()) {}  
  
    [[nodiscard]] bool has_next() const {  
        return iter_ != end_;  
    }  
  
    [[nodiscard]] int next() {  
        if (!has_next()) throw std::out_of_range("No more elements");  
        return *iter_++;  
    }  
  
private:  
    JoinView flattened_;  
    Iterator iter_, end_;  
};
range大神赢了
分析llama.cpp的溢出漏洞。文章很长,我没有耐心看完。安全从业人员感兴趣的自己看一下
import shorty;
using namespace shorty::literals;
// you no longer need to remember if it's `std::less` or `std::greater`
std::ranges::sort(subject, $lhs > $rhs);
// filter only even
subject | std::views::filter(($i % 2) == 0);
// zip together and transform
std::views::zip(A,B,C,D) | std::views::transform($a * $b + $c * $d);
// call external function
auto pythagorean = $<sqrt>($a * $a + $b * $b); // or $call<sqrt>;
pythagorean(3.0, 4.0) == 5.0;
// casting
auto to_int = $<int>($0); // or $cast<int>;
to_int(4.3) == 4; 
// remap by index
auto indices = std::vector<int>{...};
auto mapping = indices | $(data)[$i];
有点意思
放假了家人们
