公众号
点击「查看原文」跳转到 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];
有点意思
放假了家人们