C++ 中文周刊 2024-12-29 第174期

周刊项目地址

公众号

点击「查看原文」跳转到 GitHub 上对应文件,链接就可以点击了

qq群 点击进入

RSS

欢迎投稿,推荐或自荐文章/软件/资源等,评论区留言

本期文章由 Amnesia wyhqaq HNY 赞助 在此表示感谢

老板大气祝老板永远不死


资讯

标准委员会动态/ide/编译器信息放在这里

十二月邮件列表

编译器信息最新动态推荐关注hellogcc公众号 本周更新 2024-01-04 第286期

文章

Retrofitting spatial safety to hundreds of millions of lines of C++

google在 c++安全上的实践,主要是采用libc++ harden mode

非常推荐使用。我之前搞了个安全的介绍,感兴趣可以看一下

Structured Binding Upgrades in C++26

支持字段级别设置attr了 p0609

auto [it, inserted [[maybe_unused]] ] = map.try_emplace(key, value);

支持在条件中展开,类似c++17那个条件中赋值

if (auto [n] = f()) { ... }

while (auto [header, body] = receive_packet()) { ... }

展开绑定到tuple p1061

template <class T>
auto tie_as_tuple(T& x) {
    auto& [...xs] = x;
    return std::tie(xs...);
}

如果这个能支持,真是有点改变相关工具生态了 magic get/boost pfr可是做了好多脏活

How to Hash Objects Without Repetition: std::hash can be DRY

介绍hash combine和 tie组合的。

#include <string>
#include <tuple>
#include <cassert>
#include <cstdint>
#include <boost/functional/hash.hpp>
#include <iostream>

namespace some_lib {
    template <typename T>
    constexpr void hash_combine(size_t& seed, const T& value)
    {
        seed ^= std::hash<T>{}(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    }
}

template <typename... TValues>
constexpr auto combined_hash(const TValues&... values)
{
    size_t seed{};
    (..., some_lib::hash_combine(seed, values));

    return seed;
}

struct HashableForTiedMembers
{ };

template <typename T>
constexpr bool hashing_for_tied_members = false; 

template <typename T>
concept HashableForTied = std::derived_from<T, HashableForTiedMembers> || hashing_for_tied_members<T>;

template <HashableForTied T>
struct std::hash<T>
{
    size_t operator()(const T& value) const
    {
        auto hasher = [](const auto&... args) {
            return combined_hash(args...);
        };
        return std::apply(hasher, value.tied());
    }
};

template <typename T>
concept Hashable = requires {
    { std::hash<T>{}(std::declval<T>()) } -> std::convertible_to<size_t>;
};

/////////////////////////////////////////////////////////////////////////
// Person class - opt-in through inheritance

struct Person : HashableForTiedMembers
{
    std::string first_name;
    std::string last_name;
    std::uint8_t age;

    Person(std::string first_name, std::string last_name, std::uint8_t age)
        : first_name{std::move(first_name)}
        , last_name{std::move(last_name)}
        , age{age}
    {
    }

    auto tied() const
    {
        return std::tie(first_name, last_name, age);
    }

    bool operator==(const Person& other) const
    {
        return tied() == other.tied();
    }

    auto operator<=>(const Person& other) const
    {
        return tied() == other.tied();
    }
};

static_assert(Hashable<Person>);

//////////////////////////////////////////////////////////////////////////
// AggregatePerson - opt-in through variable template specialization
struct AggregatePerson
{
    std::string first_name;
    std::string last_name;
    std::uint8_t age;

    auto tied() const
    {
        return std::tie(first_name, last_name, age);
    }
};

template <>
constexpr bool hashing_for_tied_members<AggregatePerson> = true;

int main()
{
    Person p1{"John", "Doe", 33};
    Person p2{"John", "Doe", 33};
    Person p3{"John", "Don", 44};

    assert(p1 == p2);
    assert(p2 < p3);

    assert(std::hash<Person>{}(p1) == std::hash<Person>{}(p2));
    assert(std::hash<Person>{}(p1) == std::hash<Person>{}(p3));

    AggregatePerson ap1{"John", "Doe", 30};
	AggregatePerson ap2{"John", "Doe", 30};
	AggregatePerson ap3{"John", "Dog", 30};

	assert(std::hash<AggregatePerson>{}(ap1) == std::hash<AggregatePerson>{}(ap2));
	assert(std::hash<AggregatePerson>{}(ap1) != std::hash<AggregatePerson>{}(ap3));

    return std::hash<Person>{}(p1);
}

其实他这个设计就是hash_append

    template <class HashAlgorithm>
    friend void hash_append(HashAlgorithm& h, X const& x) noexcept
    {
        //
    }

他这个写法就是把hash算法和x拆出来,x本身转pack 方便归一计算,用的tie 避免开销

combined_hash其实就是hash_append 只不过算法h不能定制

不知道读者们了不了解n3980 hash_append 不了解没关系。就是函数接口定制和这里的代码一个意思

Measuring std::unordered_map Badness

非常搞笑的场景,同样的hashmap float 做key和 int做key冲突率不同 因为值域不同。float表达式导致的

针对float做key要注意hash算法

Inside STL: The atomic shared_ptr

和shared_ptr差不多,为了原子 使用tag pointer 用特殊字段做spinlock 代码在这里

	  while (!_M_val.compare_exchange_strong(__current,
						 __current | _S_lock_bit,
						 __o,
						 memory_order_relaxed))
	    {
	      _GLIBCXX_TSAN_MUTEX_TRY_LOCK_FAILED(&_M_val);
#if __glibcxx_atomic_wait
	      __detail::__thread_relax();
#endif
	      __current = __current & ~_S_lock_bit;
	      _GLIBCXX_TSAN_MUTEX_TRY_LOCK(&_M_val);
	    }
	  _GLIBCXX_TSAN_MUTEX_LOCKED(&_M_val);
	  return reinterpret_cast<pointer>(__current);
	}

Type-safe Enum Class Bit Flags

直接贴代码

#include <bitset>
#include <ostream>
#include <type_traits>
#include <utility>

// Helper class for bitwise flag-like operations on scoped enums.
//
// This class provides a way to represent combinations of enum values without
// directly overloading operators on the enum type itself. This approach
// avoids ambiguity in the type system and allows the enum type to continue
// representing a single value, while the BitFlags can hold a combination
// of enum values.
//
// Example usage:
//
// enum class MyEnum { FlagA = 1 << 0, FlagB = 1 << 1, FlagC = 1 << 2 };
//
// BitFlags<MyEnum> flags = { MyEnum::FlagA, MyEnum::FlagC };
// flags.Unset(MyEnum::FlagA);
// if (flags.IsSet(MyEnum::FlagC)) {
//   // ...
// }
//
// flags |= MyEnum::FlagB;
// BitFlags<MyEnum> new_flags = ~flags;
template <typename T>
class BitFlags {
    using UnderlyingT = std::underlying_type_t<T>;

   public:
    constexpr BitFlags() : flags_(static_cast<UnderlyingT>(0)) {}
    constexpr explicit BitFlags(T v) : flags_(ToUnderlying(v)) {}
    constexpr BitFlags(std::initializer_list<T> vs) : BitFlags() {
        for (T v : vs) {
            flags_ |= ToUnderlying(v);
        }
    }

    // Checks if a specific flag is set.
    constexpr bool IsSet(T v) const {
        return (flags_ & ToUnderlying(v)) == ToUnderlying(v);
    }
    // Sets a single flag value.
    constexpr void Set(T v) { flags_ |= ToUnderlying(v); }
    // Unsets a single flag value.
    constexpr void Unset(T v) { flags_ &= ~ToUnderlying(v); }
    // Clears all flag values.
    constexpr void Clear() { flags_ = static_cast<UnderlyingT>(0); }

    constexpr operator bool() const {
        return flags_ != static_cast<UnderlyingT>(0);
    }

    friend constexpr BitFlags operator|(BitFlags lhs, T rhs) {
        return BitFlags(lhs.flags_ | ToUnderlying(rhs));
    }
    friend constexpr BitFlags operator|(BitFlags lhs, BitFlags rhs) {
        return BitFlags(lhs.flags_ | rhs.flags_);
    }
    friend constexpr BitFlags operator&(BitFlags lhs, T rhs) {
        return BitFlags(lhs.flags_ & ToUnderlying(rhs));
    }
    friend constexpr BitFlags operator&(BitFlags lhs, BitFlags rhs) {
        return BitFlags(lhs.flags_ & rhs.flags_);
    }
    friend constexpr BitFlags operator^(BitFlags lhs, T rhs) {
        return BitFlags(lhs.flags_ ^ ToUnderlying(rhs));
    }
    friend constexpr BitFlags operator^(BitFlags lhs, BitFlags rhs) {
        return BitFlags(lhs.flags_ ^ rhs.flags_);
    }

    friend constexpr BitFlags& operator|=(BitFlags& lhs, T rhs) {
        lhs.flags_ |= ToUnderlying(rhs);
        return lhs;
    }
    friend constexpr BitFlags& operator|=(BitFlags& lhs, BitFlags rhs) {
        lhs.flags_ |= rhs.flags_;
        return lhs;
    }
    friend constexpr BitFlags& operator&=(BitFlags& lhs, T rhs) {
        lhs.flags_ &= ToUnderlying(rhs);
        return lhs;
    }
    friend constexpr BitFlags& operator&=(BitFlags& lhs, BitFlags rhs) {
        lhs.flags_ &= rhs.flags_;
        return lhs;
    }
    friend constexpr BitFlags& operator^=(BitFlags& lhs, T rhs) {
        lhs.flags_ ^= ToUnderlying(rhs);
        return lhs;
    }
    friend constexpr BitFlags& operator^=(BitFlags& lhs, BitFlags rhs) {
        lhs.flags_ ^= rhs.flags_;
        return lhs;
    }

    friend constexpr BitFlags operator~(const BitFlags& bf) {
        return BitFlags(~bf.flags_);
    }

    friend constexpr bool operator==(const BitFlags& lhs, const BitFlags& rhs) {
        return lhs.flags_ == rhs.flags_;
    }
    friend constexpr bool operator!=(const BitFlags& lhs, const BitFlags& rhs) {
        return lhs.flags_ != rhs.flags_;
    }

    // Stream output operator for debugging.
    friend std::ostream& operator<<(std::ostream& os, const BitFlags& bf) {
        // Write out a bitset representation.
        os << std::bitset<sizeof(UnderlyingT) * 8>(bf.flags_);
        return os;
    }

    // Construct BitFlags from raw values.
    static constexpr BitFlags FromRaw(UnderlyingT flags) {
        return BitFlags(flags);
    }
    // Retrieve the raw underlying flags.
    constexpr UnderlyingT ToRaw() const { return flags_; }

   private:
    constexpr explicit BitFlags(UnderlyingT flags) : flags_(flags) {}
    static constexpr UnderlyingT ToUnderlying(T v) { return static_cast<UnderlyingT>(v); }
    UnderlyingT flags_;
};

In C++, how can I make a default parameter be the this pointer of the caller?, revisited

template<typename...Args>
struct maker
{
    std::tuple<Args&&...> m_args;

    maker(Args&&... args) : m_args((Args&&)args...) {}

    template<typename T>
    operator T() {
        return std::make_from_tuple<T>(std::move(m_args));
    }
};

template<typename D>
struct OwnerHelper
{
    template<typename...Args>
    auto Owned(Args&&... args)
    { return maker<Args&&..., D*>((Args&&)args..., static_cast<D*>(this)); }
};

struct Widget : Object, OwnerHelper<Widget>
{
    Property<int> Height = Owned("Height", 10);
    Property<std::string> Name = Owned("Name", ""s);
    Event<NameChangedHandler> NameChanged = Owned("NameChanged");
};

通过crtp绕一下传this 当然如果有deducing this可以这样

struct OwnerHelper
{
    template<typename O, typename...Args>
    auto Owned(this O&& self, Args&&... args)
    {
        return maker<Args&&..., O*>((Args&&)args..., &self);
    }
};

struct Widget : Object, OwnerHelper
{
    Property<int> Height = Owned("Height", 10);
    Property<std::string> Name = Owned("Name", ""s);
    Event<NameChangedHandler> NameChanged = Owned("NameChanged");
};

Court is in session: Top 10 most notorious C and C++ errors in 2024

列举几个经典bug代码

realloc失败

int sceNetAdhocMatchingSetHelloOpt(int matchingId,
                                   int optLenAddr, u32 optDataAddr)
{
  ....
  if (optLenAddr > context->hellolen)
  {
    hello = realloc(hello, optLenAddr);
  }
  ....
}

这里realloc失败返回null正好给hello覆盖了。原来的hello泄漏

拼写错误一个例子

static const TypeToNameMap& get_type_to_name_tbl() {
  static const TypeToNameMap type_to_name_tbl = {
    ....,
    {"Abs", Type::Eltwise},                   // <=
    ....,
    {"SoftPlus", Type::Eltwise},              // <=
    ....,
    {"Abs", Type::Math},                      // <=
    ....,
    {"SoftPlus", Type::Math},                 // <=
    ...., 
  };
  return type_to_name_tbl;
}

继承忘记final导致误用,代码不列了有点蠢

死代码/复制粘贴错误也不列了

ifelse匹配

const qdGameObjectStateWalk* qdGameObjectMoving::current_walk_state() const
{
  const qdGameObjectState* st = get_cur_state();
  if(!st || st -> state_type() != qdGameObjectState::STATE_WALK){
#ifndef _QUEST_EDITOR
    st = last_walk_state_;
    if(!st || st -> state_type() != qdGameObjectState::STATE_WALK)
      st = get_default_state();
else
    st = get_default_state();
    if(!st) st = get_state(0);
#endif
  }
  ....
}

else忘记写#了。

不可见字符

static const char *script_list[][2] = {
  ....
  { "Myanmar / Burmese", "Mymr" },
  { "​Nag Mundari", "Nagm" },
  { "Nandinagari", "Nand" },
  ....
}

以及数组溢出,代码不列了

开源项目介绍

互动环节

本期更新距离上一期差了一个月,作者沉迷街霸是原因之一

另外的原因就是,感觉很累,有一点重复自己。在自媒体的内容想想搞一点突破,涨涨流量

但是比起去年接到商单,今年只接到图书推广单子,赠书,书我转手送了

比较惨淡

考虑了带货,带货金额没有押金多

另外就是蹭热度/日更段子阅读量更好。而周更内容实际上比较一般

确实迷茫了。反复重复自我,还是深耕一下,只讲专题,周刊改成月刊形式?

资料是看不完的,周刊总感觉只是泛泛而谈没啥深度内容

一方面,读者需要深度内容,一方面读者也看tips之类的知识点

我感觉我有点搞不过来,时间真不够用

欢迎大家提出建议意见


上一期 下一期

看到这里或许你有建议或者疑问或者指出错误,请留言评论! 多谢! 你的评论非常重要!也可以帮忙点赞收藏转发!多谢支持! 觉得写的不错那就给点吧, 在线乞讨 微信转账