公众号
qq群 手机qq点击进入
RSS https://github.com/wanghenshui/cppweeklynews/releases.atom
欢迎投稿,推荐或自荐文章/软件/资源等
文章大部分来自
https://discu.eu/weekly/candcpp/2023/49/
https://www.meetingcpp.com/blog/blogroll/items/Meeting-Cpp-weekly-Blogroll-408.html
本期文章由 黄亮Anthony 赞助
标准委员会动态/ide/编译器信息放在这里
编译器信息最新动态推荐关注hellogcc公众号 OSDT Weekly 2023-12-13 第232期
另外PLCT有rsicv竞赛,感兴趣的可以参加一下 rvspoc.org
boost发布1.84版本,c++03全部抛弃,windows只支持win10及以上
新增redis cobalt库之前讲过
另外asio移除了execution相关设计。。
PFR支持fieldname 反射,要求c++20 https://github.com/boostorg/pfr/pull/129/files
效果 https://godbolt.org/z/xbo7bos86
#include <https://raw.githubusercontent.com/denzor200/pfr/amalgamate_get_name/include/boost/pfr/gen/pfr.hpp>
#include <functional>
#include <cstdio>
#include <cstring>
struct Any {
Any() {};
};
struct XClass {
int member1;
Any this_is_a_name; // not constexpr constructible
std::reference_wrapper<char> c; // not default constructible
};
int main() {
char buf[32] {0};
constexpr auto first = boost::pfr::get_name<0, XClass>();
memcpy(buf, first.data(), first.size());
puts(buf);
static_assert("member1" == boost::pfr::get_name<0, XClass>());
static_assert("this_is_a_name" == boost::pfr::get_name<1, XClass>());
static_assert("c" == boost::pfr::get_name<2, XClass>());
}
Unordered支持concurrent_flat_set以及并发visit
其他的没啥说的。自己看吧
https://www.boost.org/users/history/version_1_84_0.html
代码在这里 https://github.com/cdacamar/fredbuf
手把手教你实现编辑器
在130期咱们就聊过,如果cacheline 64,设置align 128能降低影响。lemire给了一种简单的测试方法,拷贝数组
https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2023/12/12/cacheline.c
小于cacheline,带宽没啥区别,受cacheline影响了,大于cacheline,越大越快,理论上加入cacheline 64 拷贝128,应该获得翻倍的速度,但是实际上并不是
建议大家自己玩玩,测一下效果。在m1表现也不太一样,但是小于cacheline拷贝速度不变这个现象是不变的
140期的调度器实现有bug,问题代码
class Scheduler {
std::priority_queue<job, std::vector<job>, Comperator> _prioTasks;
public:
void emplace(int prio, std::coroutine_handle<> task) {
_prioTasks.push(std::make_pair(prio, task));
}
};
Task createTask(const std::string& name) {
std::cout << name << " start\n";
co_await std::suspend_always();
for (int i = 0; i <= 3; ++i ) {
std::cout << name << " execute " << i << "\n"; // (5)
co_await std::suspend_always();
}
co_await std::suspend_always();
std::cout << name << " finish\n";
}
scheduler1.emplace(0, createTask("TaskA").get_handle());
看出来哪里有问题没有?createtask 的name的生命周期问题
成员函数参数不能直接用this
struct Sample
{
int increment;
void add(int v = increment); // not allowed
void notify_all(Sample* source = this); // not allowed
};
猥琐绕过
struct Sample
{
int increment;
void add(int v);
void add() { add(increment); }
void notify_all(Sample* source);
void notify_all() { notify_all(this); }
};
Sample s;
s.add(2); // adds 2
s.add(); // adds s.increment
s.notify_all(); // uses source = s
s.notify_all(other); // uses source = other
随机浮点数
比如 这个实现 https://dotat.at/@/2023-06-23-random-double.html
double pcg64_random_double(pcg64_t *rng) {
return (double)(pcg64_random(rng) >> 11) * 0x1.0p-53;
}
luajit是这样的
uint64_t lj_prng_u64d(PRNGState *rs) {
uint64_t z, r = 0;
TW223_STEP(rs, z, r)
/* Returns a double bit pattern in the range 1.0 <= d < 2.0. */
return (r & 0x000fffffffffffffull) | 0x3ff0000000000000ull;
}
/* Then to give d in [0, 1) range: */
U64double u;
double d;
u.u64 = lj_prng_u64d(rs);
d = u.d - 1.0;
lemire博士的golang版本
// toFloat64 -> [0,1)
func toFloat64(seed *uint64) float64 {
x := splitmix64(seed)
x &= 0x1fffffffffffff // %2**53
return float64(x) / float64(0x1fffffffffffff)
}
原理是这个 https://www.zhihu.com/question/25037345/answer/29879012
01之间
double rand_between_zero_and_one() {
double d;
uint64_t x = rand_u64() >> 11; /* 53-bit uniform integer */
uint64_t e = 1022;
do {
if (rand_u64() & 1) break; /* 1-bit uniform integer */
e -= 1;
} while (e > 1022-75);
x = ((x + 1) >> 1) + (e << 52);
memcpy(&d, &x, sizeof(d));
return d;
}
优化
double rand_between_zero_and_one() {
double d;
uint64_t x = rand_u64();
uint64_t e = __builtin_ctzll(x) - 11ull;
if ((int64_t)e >= 0) e = __builtin_ctzll(rand_u64());
x = (((x >> 11) + 1) >> 1) - ((e - 1011ull) << 52);
memcpy(&d, &x, sizeof(d));
return d;
}
主要是要懂浮点数格式以及如何恰当的均匀分布
contains
#include <string>
#include <iostream>
int main(){
const std::string url = "https://isocpp.org";
if (url.contains("https") &&
url.contains(".org") &&
url.contains("isocpp"))
std::cout << "you're using the correct site!\n";
}
starts_with(), ends_with()
insert range
#include <iostream>
#include <iterator>
#include <string>
int main() {
const auto source = {'l', 'i', 'b', '_'};
std::string target{"__cpp_containers_ranges"};
const auto pos = target.find("container");
auto iter = std::next(target.begin(), pos);
#ifdef __cpp_lib_containers_ranges
target.insert_range(iter, source);
#else
target.insert(iter, source.begin(), source.end());
#endif
std::cout << target;
}
spanstream
#include <iostream>
#include <sstream>
#include <spanstream> // << new headeer!
void* operator new(std::size_t sz){
std::cout << "Allocating: " << sz << '\n';
return std::malloc(sz);
}
int main() {
std::cout << "start...\n";
std::stringstream ss;
ss << "one string that doesn't fit into SSO";
ss << "another string that hopefully won't fit";
std::cout << "spanstream:\n";
char buffer[128] { 0 };
std::span<char> spanBuffer(buffer);
std::basic_spanstream<char> ss2(spanBuffer);
ss2 << "one string that doesn't fit into SSO";
ss2 << "another string that hopefully won't fit";
std::cout << buffer;
}
想了解cpython的可以看看
介绍spanstream的, 直接贴代码了
#include <iostream>
#include <span>
#include <spanstream>
#include <cassert>
void printSpan(auto spanToPrint) {
for (size_t i = 0; i < spanToPrint.size(); ++i) {
std::cout << spanToPrint[i];
}
}
void useSpanbuf() {
std::array<char, 16> charArray;
std::span<char, 16> charArraySpan(charArray);
std::spanbuf buf;
char c = 'a';
for (size_t i = 0; i < 16; ++i) {
charArraySpan[i] = c;
++c;
}
buf.span(charArraySpan);
// we can easily print a span got from the buffer
std::span bufview = buf.span();
std::cout << "bufview: ";
for (size_t i = 0; i < 16; ++i) {
std::cout << bufview[i];
}
std::cout << '\n';
}
void useSpanstream() {
std::array<char, 16> charArray;
std::ospanstream oss(charArray);
oss << "Fortytwo is " << 42;
// copying the contents to a span
std::string s{oss.span().data(),size_t(oss.span().size())};
assert(s == "Fortytwo is 42");
}
int main() {
useSpanbuf();
useSpanstream();
return 0;
}
Raymond chen环节。我直接贴连接了。window是我不懂
linux环节
shmem tmpfs 比较经典
linux 5.6引入的,有意思
姚奕正qds推荐
把 reflection/injection, pattern matching, senders都说了一遍,可以算是一个完全的新c++
在指针上做计算风险高,这也是为啥要引入span stringview,不用char *
信息丢失太多
cppcon2023
cpponsea 2023
考虑一个场景,
std::variant<int, float> foo();
std::variant<int, float,double> bar(){
auto v = foo();
...
}
显然涉及到从std::variant不同参数的转换,怎么写?
template<class From, class To>
constexpr To variant_cast(From&& from) {
return std::visit([](auto&& a) constexpr noexcept(
std::is_nothrow_constructible_v<
To,
std::in_place_type_t<std::remove_cvref_t<decltype(a)>>,
decltype(a)
>) -> To {
return To(
std::in_place_type<std::remove_cvref_t<decltype(a)>>,
std::forward<decltype(a)>(a));
},
std::foward<From>(from)
);
}
存在一个问题,就是从std::variant<int, float>
到std::variant<int, double>
没法转,太safe了,得放宽一点
template<class To>
struct visitor {
template<class T>
requires std::constructible_from<To,std::in_place_type_t<std::remove_cvref_t<T>>,T>
constexpr To operator()(T&& t) const noexcept(
std::is_nothrow_constructible_v<To,std::in_place_type_t<std::remove_cvref_t<T>>,T>){
return To(
std::in_place_type<std::remove_cvref_t<decltype(a)>>,
std::forward<T>(t));
}
template<class T>
constexpr To operator()(T&& t) const noexcept {
std::unreachable();
}
};
template<class To, class From>
constexpr To uncheck_variant_cast(From&& from) {
return std::visit(visitor<To>{}, std::forward<From>(from));
}
其实就是舍弃内部值本来的类型,用From硬推,匹配不到就失败
问题来了,如果是std::variant<const int, float>
怎么办?
别琢磨了,能做,不支持,去他妈的,你就非得这么写吗,No
Unilang deepin的一个通用编程语言,点子有点意思,也缺人,感兴趣的可以github讨论区或者deepin论坛看一看。这里也挂着长期推荐了
上一期提到的有问题的代码
int median(std::vector<int>& v) {
int mid = v.size() / 2;
std::nth_element(v.begin(), v.begin() + mid, v.end());
int result = v[mid];
if (v.size() % 2 == 0) {
std::nth_element(v.begin(), v.begin() + mid - 1, v.end());
result = (v[mid] + v[mid-1])/2;
// result = (result + v[mid-1]) /2;
}
return result;
}
周星星指出,完全可以第二个std::nth_element改成 std::max_element(begin,begin+mid)
确实是一个思路,或者更极端一点,偶数也不特殊处理就好了
huring指出这个问题在macbook m1上不复现我的提供的 10,10,29,18,10,10,10,10,13,32无法复现
只能说和平台也有点关系,巧合而已
突击提问:如何实现nth_element?最近面试刚被问到,我没上来优化的部分。已经自罚十个深蹲了