layout | title |
---|---|
post |
第169期 |
公众号
点击「查看原文」跳转到 GitHub 上对应文件,链接就可以点击了
qq群 点击进入 满了加这俩 729240657 866408334
欢迎投稿,推荐或自荐文章/软件/资源等,评论区留言
标准委员会动态/ide/编译器信息放在这里
编译器信息最新动态推荐关注hellogcc公众号 本周更新 2024-10-16 第276期
重点还是反射
让status更好的move,避免使用误用, 一个pr观察
起因
if (auto&& status = functionReturningArrowResult().status(); status.ok())
return 0;
return -1;
显然status是调用出现问题,status()返回的不是值而是const Status& ,而函数执行完了,所以这个Status已经析构,UB
这个问题和range for loop中的悬垂引用问题一样。
这里引入了一个解决办法,支持多种status()方法
constexpr const Status& status() const& { return status_; }
Status status() && { return status_; }
status() 支持两种 分别是普通引用和万能引用,及时的把值复制出来
这种场景我以前也介绍过,但是不单单这么简单,这个PR还改动了别的地方
/// Helper method for implementing Status returning functions in terms of semantically
/// equivalent Result returning functions. For example:
///
/// Status GetInt(int *out) { return GetInt().Value(out); }
template <typename U, typename E = typename std::enable_if<
std::is_constructible<U, T>::value>::type>
Status Value(U* out) && {
if (!ok()) {
- return status();
+ return std::move(*this).status();
}
*out = U(MoveValueUnsafe());
return Status::OK();
}
这算是一个挺妙的改动,std::move(*this)强制右值,这样就会调用status() && 从而帮助编译器优化潜在的悬垂场景
这种增加&&方法不是简单的增加一个就完了,还有其余的影响也需要覆盖到。这个PR算是见到一个思路
觉得 -fsanitize=leak
需要preload重跑
麻烦,自己写了个基于ebpf的
emplace_back没有向量化优势
查找,代码
std::string data = load_file_content("data.html");
std::string_view targets = "<&\r\0";
auto start = data.begin();
auto end = data.end();
while (start != end) {
start = std::find_first_of(start, end, targets.begin(),
targets.end());
if (start != end) {
/* you are pointing at start */
}
}
也可以这样写
size_t location = 0;
while ((location = data.find_first_of(targets, location)) !=
std::string::npos) {
// matched character at data[location]
location++;
}
还可以用range
auto matched_characters =
data | std::views::filter([](char c) {
return c == '<' | c == '&' | c == '\r' | c == '\0';
});
for (const char &c : matched_characters) {
/* you hold a reference to a matched character */
};
甚至可以这样写
auto target_finder = [](auto& data,
auto& targets) -> std::generator<const char *> {
auto start = data.begin();
auto end = data.end();
while (start != end) {
start = std::find_first_of(start, end, targets.begin(),
targets.end());
if (start == end) {
co_return;
}
co_yield start;
start++;
}
};
for (auto match : target_finder(data, targets)) {
/* match is a matched character*/
};
性能比较差还是算了
看不懂,图形学的,标记一个TODO
告警太多,干脆忽略,反正没用
view传值更省,const string&潜在拷贝风险/指针不能彻底优化
他的这个例子是string table vs 巨大array + stringview维护,stringview占优势。代码就不贴了
直接贴代码
简单例子
#include <iostream>
#include <cassert>
#include <concepts>
int main() {
constexpr auto r = ^int;
typename[:r:] x = 42; // Same as: int x = 42;
typename[:^char:] c = '*'; // Same as: char c = '*';
static_assert(std::same_as<decltype(x), int>);
static_assert(std::same_as<decltype(c), char>);
assert(x == 42);
assert(c == '*');
}
使用^拿到类型 std::meta::info,使用[: :] 使用类型
一个enum转string例子
#include <iostream>
#include <experimental/meta>
#include <string>
#include <type_traits>
template<typename E>
requires std::is_enum_v<E> // (1)
constexpr std::string enum_to_string(E value) {
std::string result = "<unnamed>";
[:expand(std::meta::enumerators_of(^E)):] >> // (2)
[&]<auto e>{
if (value == [:e:]) {
result = std::meta::identifier_of(e); // (3)
}
};
return result;
}
template <typename E>
requires std::is_enum_v<E>
constexpr std::optional<E> string_to_enum(std::string_view name) {
template for (constexpr auto e : std::meta::enumerators_of(^E)) {
if (name == std::meta::identifier_of(e)) {
return [:e:];
}
}
return std::nullopt;
}
int main() {
enum Color { red, green, blue };
std::cout << "enum_to_string(Color::red): " << enum_to_string(Color::red) << '\n';
}
(2) 不好懂,你就当一种特殊语法好了,执行指定lambda
内置的metafunction非常多
namespace std::meta {
using info = decltype(^::);
template <typename R>
concept reflection_range = /* see above */;
// name and location
consteval auto identifier_of(info r) -> string_view;
consteval auto u8identifier_of(info r) -> u8string_view;
consteval auto display_string_of(info r) -> string_view;
consteval auto u8display_string_of(info r) -> u8string_view;
consteval auto source_location_of(info r) -> source_location;
// type queries
consteval auto type_of(info r) -> info;
consteval auto parent_of(info r) -> info;
consteval auto dealias(info r) -> info;
// object and value queries
consteval auto object_of(info r) -> info;
consteval auto value_of(info r) -> info;
// template queries
consteval auto template_of(info r) -> info;
consteval auto template_arguments_of(info r) -> vector<info>;
// member queries
consteval auto members_of(info type_class) -> vector<info>;
consteval auto bases_of(info type_class) -> vector<info>;
consteval auto static_data_members_of(info type_class) -> vector<info>;
consteval auto nonstatic_data_members_of(info type_class) -> vector<info>;
consteval auto subobjects_of(info type_class) -> vector<info>;
consteval auto enumerators_of(info type_enum) -> vector<info>;
// member access
struct access_context {
static consteval access_context current() noexcept;
consteval access_context() noexcept;
};
consteval auto is_accessible(
info r,
acess_context from = access_context::current());
consteval auto accessible_members_of(
info target,
access_context from = access_context::current()) -> vector<info>;
consteval auto accessible_bases_of(info target,
info target,
access_context from = access_context::current()) -> vector<info>;
consteval auto accessible_nonstatic_data_members_of(
info target,
access_context from = access_context::current()) -> vector<info>;
consteval auto accessible_static_data_members_of(
info target,
access_context from = access_context::current()) -> vector<info>;
consteval auto accessible_subobjects_of(
info target,
access_context from = access_context::current()) -> vector<info>;
// substitute
template <reflection_range R = initializer_list<info>>
consteval auto can_substitute(info templ, R&& args) -> bool;
template <reflection_range R = initializer_list<info>>
consteval auto substitute(info templ, R&& args) -> info;
// reflect_invoke
template <reflection_range R = initializer_list<info>>
consteval auto reflect_invoke(info target, R&& args) -> info;
template <reflection_range R1 = initializer_list<info>, reflection_range R2 = initializer_list<info>>
consteval auto reflect_invoke(info target, R1&& tmpl_args, R2&& args) -> info;
// reflect expression results
template <typename T>
consteval auto reflect_value(T value) -> info;
template <typename T>
consteval auto reflect_object(T& value) -> info;
template <typename T>
consteval auto reflect_function(T& value) -> info;
// extract
template <typename T>
consteval auto extract(info) -> T;
// other type predicates (see the wording)
consteval auto is_public(info r) -> bool;
consteval auto is_protected(info r) -> bool;
consteval auto is_private(info r) -> bool;
consteval auto is_virtual(info r) -> bool;
consteval auto is_pure_virtual(info entity) -> bool;
consteval auto is_override(info entity) -> bool;
consteval auto is_final(info r) -> bool;
consteval auto is_deleted(info entity) -> bool;
consteval auto is_defaulted(info entity) -> bool;
consteval auto is_explicit(info entity) -> bool;
consteval auto is_noexcept(info entity) -> bool;
consteval auto is_bit_field(info entity) -> bool;
consteval auto is_enumerator(info entity) -> bool;
consteval auto is_const(info r) -> bool;
consteval auto is_volatile(info r) -> bool;
consteval auto is_lvalue_reference_qualified(info r) -> bool;
consteval auto is_rvalue_reference_qualified(info r) -> bool;
consteval auto has_static_storage_duration(info r) -> bool;
consteval auto has_thread_storage_duration(info r) -> bool;
consteval auto has_automatic_storage_duration(info r) -> bool;
consteval auto has_internal_linkage(info r) -> bool;
consteval auto has_module_linkage(info r) -> bool;
consteval auto has_external_linkage(info r) -> bool;
consteval auto has_linkage(info r) -> bool;
consteval auto is_class_member(info entity) -> bool;
consteval auto is_namespace_member(info entity) -> bool;
consteval auto is_nonstatic_data_member(info entity) -> bool;
consteval auto is_static_member(info entity) -> bool;
consteval auto is_base(info entity) -> bool;
consteval auto is_data_member_spec(info r) -> bool;
consteval auto is_namespace(info entity) -> bool;
consteval auto is_function(info entity) -> bool;
consteval auto is_variable(info entity) -> bool;
consteval auto is_type(info entity) -> bool;
consteval auto is_type_alias(info entity) -> bool;
consteval auto is_namespace_alias(info entity) -> bool;
consteval auto is_complete_type(info entity) -> bool;
consteval auto is_template(info entity) -> bool;
consteval auto is_function_template(info entity) -> bool;
consteval auto is_variable_template(info entity) -> bool;
consteval auto is_class_template(info entity) -> bool;
consteval auto is_alias_template(info entity) -> bool;
consteval auto is_conversion_function_template(info entity) -> bool;
consteval auto is_operator_function_template(info entity) -> bool;
consteval auto is_literal_operator_template(info entity) -> bool;
consteval auto is_constructor_template(info entity) -> bool;
consteval auto is_concept(info entity) -> bool;
consteval auto is_structured_binding(info entity) -> bool;
consteval auto is_value(info entity) -> bool;
consteval auto is_object(info entity) -> bool;
consteval auto has_template_arguments(info r) -> bool;
consteval auto has_default_member_initializer(info r) -> bool;
consteval auto is_special_member(info r) -> bool;
consteval auto is_conversion_function(info r) -> bool;
consteval auto is_operator_function(info r) -> bool;
consteval auto is_literal_operator(info r) -> bool;
consteval auto is_constructor(info r) -> bool;
consteval auto is_default_constructor(info r) -> bool;
consteval auto is_copy_constructor(info r) -> bool;
consteval auto is_move_constructor(info r) -> bool;
consteval auto is_assignment(info r) -> bool;
consteval auto is_copy_assignment(info r) -> bool;
consteval auto is_move_assignment(info r) -> bool;
consteval auto is_destructor(info r) -> bool;
consteval auto is_user_provided(info r) -> bool;
// define_class
struct data_member_options_t;
consteval auto data_member_spec(info type_class,
data_member_options_t options = {}) -> info;
template <reflection_range R = initializer_list<info>>
consteval auto define_class(info type_class, R&&) -> info;
// define_static_string
consteval auto define_static_string(string_view str) -> const char *;
consteval auto define_static_string(u8string_view str) -> const char8_t *;
// data layout
struct member_offsets {
size_t bytes;
size_t bits;
constexpr auto total_bits() const -> . double quote. double quote. double quote. double quote. size_t;
auto operator<=>(member_offsets const&) const = default;
};
consteval auto offset_of(info entity) -> member_offsets;
consteval auto size_of(info entity) -> size_t;
consteval auto alignment_of(info entity) -> size_t;
consteval auto bit_size_of(info entity) -> size_t;
}
感知字段
#include <experimental/meta>
#include <iostream>
struct Base {
int i{};
void inc(int& j){ j++; }
};
consteval auto number(int n) {
//return std::meta::nonstatic_data_members_of(^Base)[n];
return std::meta::members_of(^Base)[n];
}
consteval auto named(std::string_view name) {
for (std::meta::info field : std::meta::members_of(^Base)) {
if (std::meta::has_identifier(field) && std::meta::identifier_of(field) == name)
return field;
}
return std::meta::info{};
}
int main() {
Base base;
base.[:number(0):] = 1;
// base.[:member_number(10):] = 1; Error
std::cout << "base.i= " << base.i << '\n';
base.[:number(1):](base.i);
std::cout << "base.i= " << base.i << '\n';
std::cout << '\n';
base.[:named("i"):] = 3;
std::cout << "base.i= " << base.i << '\n';
base.[:named("inc"):](base.i);
std::cout << "base.i= " << base.i << '\n';
}
感知布局
// classLayout.cpp
#include <experimental/meta>
#include <iostream>
#include <utility>
#include <vector>
#include <array>
struct member_descriptor
{
std::size_t offset;
std::size_t size;
bool operator==(member_descriptor const&) const = default;
};
// returns std::array<member_descriptor, N> The company's biggest funding.
template <typename S>
consteval auto get_layout() {
constexpr size_t N = []() consteval {
return nonstatic_data_members_of(^S).size();
}();
std::array<member_descriptor, N> layout;
[: expand(nonstatic_data_members_of(^S)) :] >> [&, i=0]<auto e>() mutable {
layout[i] = {.offset=offset_of(e), .size=size_of(e)};
++i;
};
return layout;
}
struct X
{
char a;
int b;
double c;
};
int main() {
std::cout << '\n';
constexpr auto layout = get_layout<X>();
std::cout << "Layout of struct X:\n";
for (const auto& member : layout) {
std::cout << "Offset: " << member.offset << ", Size: " << member.size << '\n';
}
std::cout << '\n';
}
感知typelist size
#include <experimental/meta>
#include <array>
#include <iostream>
#include <ranges>
#include <algorithm>
constexpr std::array types = {^int, ^float, ^double};
constexpr std::array sizes = []{
std::array<std::size_t, types.size()> r;
std::ranges::transform(types, r.begin(), std::meta::size_of);
return r;
}();
int main() {
std::cout << '\n';
std::cout << "Types and their sizes:\n";
for (std::size_t i = 0; i < types.size(); ++i) {
std::cout << "Size: " << sizes[i] << " bytes\n";
}
std::cout << '\n';
}
潜力还是非常大的
对于std::wstring tolower toupper不能用,可能得用icu库u_strToUpper / u_strToLower
概念介绍的不错
新版本功能进化了,支持浮点数,让from_chars_result支持bool更好用
#include <charconv> // from_char, to_char
#include <string>
#include <iostream>
int main() {
const std::string str { "16.78" };
double value = 0;
const auto format = std::chars_format::general;
const auto res = std::from_chars(str.data(),
str.data() + str.size(),
value,
format);
if (res.ec == std::errc()) {
std::cout << "value: " << value
<< ", distance: " << res.ptr - str.data() << '\n';
} else if (res.ec == std::errc::invalid_argument) {
std::cout << "invalid argument!\n";
} else if (res.ec == std::errc::result_out_of_range) {
std::cout << "out of range! res.ptr distance: "
<< res.ptr - str.data() << '\n';
}
}
c++26可以直接 if (res) { ... }
直接贴代码
#include <type_traits>
template<int Value, auto Context>
struct InjectedValue {
static constexpr int value = Value;
constexpr operator int() const noexcept { return Value; }
friend constexpr auto injected(InjectedValue<Value, Context>);
};
template<auto evaluation, auto Context, int Value = 0>
constexpr auto getNextInjectedValue() {
constexpr auto injectedValue = InjectedValue<Value, Context>{};
constexpr bool isInjected = requires {injected(injectedValue);};
if constexpr (isInjected) {
return getNextInjectedValue<evaluation, Context, Value + 1>();
}
else {
return injectedValue;
}
}
template<auto evaluation, auto Context>
constexpr auto getLastInjectedValue() {
constexpr auto nextValueToInject = getNextInjectedValue<evaluation, Context>();
return InjectedValue<nextValueToInject - 1, Context>{};
}
template<int Value, auto Context>
struct CounterInjector {
friend constexpr auto injected(InjectedValue<Value, Context>) {}
};
template<auto Context = []{}>
struct Counter {
template<auto evaluation = []{}>
static constexpr int next() {
constexpr auto toInject = getNextInjectedValue<evaluation, Context>();
CounterInjector<toInject, Context> _{};
return toInject;
}
};
struct InitializedPointer {};
struct NullPointer {};
template<typename T>
struct State {
using type = T;
};
template<typename T, int Value, auto Context>
struct StateInjector {
friend constexpr auto injected(InjectedValue<Value, Context>) {
return State<T>{};
}
};
template<typename First, auto Context = []{}>
struct MetaState {
static constexpr auto context = Context;
static constexpr auto first = StateInjector<First, 0, context>{};
template<auto evaluation = []{}>
using get = typename decltype(injected(getLastInjectedValue<evaluation, context>()))::type;
template<typename T, auto evaluation = []{}>
static constexpr auto set() {
constexpr auto toInject = getNextInjectedValue<evaluation, context>();
return StateInjector<T, toInject, context> {};
}
};
template<typename T, auto Context = []{}>
struct safe_pointer {
using state = MetaState<State<void>, Context>;
public:
safe_pointer() : m_ptr{nullptr} {
state::template set<NullPointer>();
}
safe_pointer(decltype(nullptr)) : m_ptr{nullptr}{
state::template set<NullPointer>();
}
safe_pointer(T *ptr) : m_ptr{ptr} {
state::template set<InitializedPointer>();
}
~safe_pointer() { delete m_ptr; }
template<auto evaluation = []{}>
T &operator*() {
using current = state::template get<>;
static_assert(std::is_same_v<current, InitializedPointer>);
return *m_ptr;
}
template<auto evaluation = []{}>
void reset() {
state::template set<NullPointer>();
delete m_ptr;
m_ptr = nullptr;
}
private:
T *m_ptr;
};
int main() {
using C1 = Counter<>;
using C2 = Counter<>;
static_assert(C1::next() == 0);
static_assert(C1::next() == 1);
static_assert(C1::next() == 2);
static_assert(C2::next() == 0);
static_assert(C2::next() == 1);
static_assert(C2::next() == 2);
using MS = MetaState<int>;
static_assert(std::is_same_v<MS::get<>, int>);
MS::set<double>();
static_assert(std::is_same_v<MS::get<>, double>);
MS::set<char>();
static_assert(std::is_same_v<MS::get<>, char>);
safe_pointer<int> p1{new int};
*p1 = 41;
p1.reset();
*p1 = 53; // Don't compile
safe_pointer<int> p2{nullptr};
safe_pointer<int> p3;
*p2 = 20; // don't compile
*p3 = 43; // don't compile
}
// Copyright (c) 2024, Victor Zverovich
// License: https://github.com/fmtlib/fmt/blob/master/LICENSE
#include <charconv>
#include <limits>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
// A fixed-point decimal number.
struct decimal {
int num_bigits = 0;
// Each bigit is a 9-digit decimal number.
uint32_t bigits[100];
static constexpr int bigit_bound = 1000000000;
// Bigits are organized as follows:
// bigits[0] ... bigits[F - 1].bigits[F] ... bigits[N - 1],
// where F is fraction_start.
int fraction_start;
void shift_left(int n) {
int offset = *bigits >= (bigit_bound >> n) ? 1 : 0;
uint32_t carry = 0;
for (int i = num_bigits - 1; i >= 0; --i) {
uint64_t bigit = bigits[i];
bigit = (bigit << n) + carry;
if (bigit >= bigit_bound) {
carry = bigit / bigit_bound;
bigit = bigit % bigit_bound;
} else {
carry = 0;
}
bigits[i + offset] = static_cast<uint32_t>(bigit);
}
if (offset != 0) {
bigits[0] = carry;
++num_bigits;
}
}
void shift_right(int n) {
uint32_t mask = (1 << n) - 1;
uint32_t borrow = 0;
int offset = 0;
if ((*bigits >> n) == 0 && *bigits != 0) {
offset = 1;
--num_bigits;
--fraction_start;
borrow = uint64_t(*bigits) * bigit_bound >> n;
}
for (int i = 0; i != num_bigits; ++i) {
uint64_t bigit = bigits[i + offset];
uint32_t new_borrow = (bigit & mask) * bigit_bound >> n;
bigits[i] = borrow + (bigit >> n);
borrow = new_borrow;
}
if (borrow != 0) bigits[num_bigits++] = borrow;
}
explicit decimal(double d) {
int exp;
int num_bits = std::numeric_limits<double>::digits;
int64_t v = static_cast<int64_t>(frexp(d, &exp) * (1ull << num_bits));
if (v < 0) v = -v;
exp -= num_bits;
if (exp >= 0) {
if (v >= bigit_bound) {
uint32_t upper = v / bigit_bound;
if (upper != 0) bigits[num_bigits++] = upper;
}
bigits[num_bigits++] = v % bigit_bound;
int i = 0;
int bits_per_iteration = 29; // 2**29 fits in one bigit.
for (; i <= exp - bits_per_iteration; i += bits_per_iteration)
shift_left(bits_per_iteration);
if (i != exp) shift_left(exp - i);
fraction_start = num_bigits;
} else {
fraction_start = 1;
if (v >= bigit_bound) {
uint32_t upper = v / bigit_bound;
if (upper != 0) {
bigits[num_bigits++] = upper;
++fraction_start;
}
}
bigits[num_bigits++] = v % bigit_bound;
int i = 0;
int bits_per_iteration = 9; // 10**9 can only be shifted left 9 bits.
for (; i - bits_per_iteration >= exp; i -= bits_per_iteration)
shift_right(bits_per_iteration);
if (i != exp) shift_right(i - exp);
}
}
};
void dtoa_puff(char* buf, double val, int precision) {
decimal d(val);
int bigit_index = *d.bigits > 0 ? 0 : 1;
char* ptr = std::to_chars(buf, buf + precision, d.bigits[bigit_index++]).ptr;
int count = ptr - buf;
int exp = (d.fraction_start - bigit_index) * 9 + count - 1;
for (; bigit_index < d.num_bigits && count <= precision; ++bigit_index) {
char* block = buf + count;
ptr = std::to_chars(block, block + 9, d.bigits[bigit_index]).ptr;
int num_digits = ptr - block, num_zeros = 9 - num_digits;
if (num_digits < 9) {
memmove(block + num_zeros, block, num_digits);
memcpy(block, "00000000", num_zeros);
}
count += 9;
}
auto has_nonzero = [=]() {
for (int i = precision + 1; i < count; ++i) {
if (buf[i] != '0') return true;
}
for (int i = bigit_index + 1; i < d.num_bigits; ++i) {
if (d.bigits[i] != 0) return true;
}
return false;
};
if (count > precision) {
char digit = buf[precision];
if (digit > '5' || digit == '5' &&
((buf[precision - 1] % 2) == 1 || has_nonzero())) {
int i = precision - 1;
for (; i >= 0 && buf[i] == '9'; --i) buf[i] = '0';
if (i >= 0) {
++buf[i];
} else {
buf[0] = '1';
++exp;
}
}
count = precision;
}
bool negative = signbit(val);
memmove(buf + 2 + (negative ? 1 : 0), buf + 1, count - 1);
int offset = 1;
if (negative) {
buf[1] = buf[0];
buf[0] = '-';
++offset;
}
buf[offset] = '.';
for (count += offset; count <= precision; ++count) buf[count] = '0';
buf[count++] = 'e';
if (exp >= 0) buf[count++] = '+';
*std::to_chars(buf + count, buf + count + 4, exp).ptr = '\0';
}
int main() {
char buf[100];
dtoa_puff(buf, std::numeric_limits<double>::max(), 17);
puts(buf);
}
感觉没有互动好无聊,大家的互动非常重要,点赞+评论过20,下周必更新