C++ 中文周刊 第28期

reddit/hackernews/lobsters/meetingcpp摘抄一些c++动态。

每周更新

周刊项目地址 github在线地址知乎专栏

欢迎投稿,推荐或自荐文章/软件/资源等,请提交 issue


资讯

编译器信息最新动态推荐关注hellogcc公众号

本周周报github直达

文章

一个游戏公司使用c++的经验分享

  1. 可读性

    1. 使用snake_case
    2. 不用简写
    3. 注释

    例子

    int32 new_held_milliseconds= update_context->get_timestamp_milliseconds() - m_start_hold_timestamp_milliseconds;
       
    set_output_property_value_and_accumulate(
        &m_current_held_milliseconds,
        new_held_milliseconds,
        &change_flags,
        FLAG(_input_event_listener_change_flag_current_held_milliseconds));
       
    bool should_trigger_hold_event= m_total_hold_milliseconds > NONE &&
        m_current_held_milliseconds > m_total_hold_milliseconds &&
        !m_flags.test(_flag_hold_event_triggered);
       
    if (should_trigger_hold_event)
    {
        // Raise a flag to emit the hold event during event processing, and another
        // to prevent emitting more events until the hold is released
        m_flags.set(_flag_hold_event_desired, true);
        m_flags.set(_flag_hold_event_triggered, true);
    }
    
  2. 格式统一

    1. 英语拼写用美式英语 color colour的区别
    2. 用i++而不是++i
    3. *和&靠近变量而不是靠近类型

    ~emmm啥品味啊这个~

  3. 还是格式

    1. 宏大写
    2. 赋值不留空格
    void c_screen_manager::render()
    {
        bool ui_rendering_enabled= true;
       
    #ifdef UI_DEBUG_ENABLED
        const c_ui_debug_globals *debug_globals= ui::get_debug_globals();
       
        if (debug_globals != nullptr && debug_globals->render.disabled)
        {
            ui_rendering_enabled= false;
        }
    #endif // UI_DEBUG_ENABLED
       
        if (ui_rendering_enabled)
        {
            // ...
        }
    }
    

    ~emmmm啥品味啊这~

  4. 充分利用名字符号

    1. ‪c_ 表示class, ‪e_ 表示枚举 m_ 表示成员变量, ‪k_表示常量
    2. 函数名,get_blank(),try_get_blank要有如名字一样稳定的表现,让使用者放心
  5. 维护性

    1. 声明直接初始化
    2. 函数一个return
    3. 有assert保证状态正确性
    4. 不用默认数组类型用其他实现的容器

    ~emmm最后一条不明白缘由~

  6. 资源管理中心话

    1. 内部内存分配器,不直接调用malloc
    2. 不用stl
int main() {
  constexpr int array[] = {1, 2, 3};
  assert(2[array] == array[2]);
  assert(*(array+1) == array[1]);
}

这学c的没有不知道的吧。没啥用反正。注意这个坑吧

介绍boost::hana的使用这里有个cheatsheet 元编程,编译器计算,类型和值混起来保存,类似场景,boost::hana是一个好用的现代的库

介绍继承模式

作者遇到的场景

struct heart {

	void pump_blood(){ std::cout << "baboom, baboom\n"; }
	
	bool consume_sugar(){ 
		if (sugar_level < 0)
			return false;
		--sugar_level;
		return true;
	}
	
	int sugar_level = 100;
};

template <class Base>
struct legs : Base {
	void move_legs(){ 
		this->pump_blood();
		std::cout << "legs are moving...\n"; 
	}
};

template <class Base>
struct body : Base {
	
	// hopefully Base has all the methods we need
	void move(float dx, float dy){
		if (not this->consume_sugar())
			return;
		this->move_legs();
		position[0] += dx;
		position[1] += dy;
	}
	
	float position[2] = {0, 0};
};
using final_body = body<legs<heart>>;

using final_body = body<legs<heart>>;这种是单向的依赖,如何抽出一套组合模版?

这里直接放代码

include <utility>
#include <type_traits>

template <class... Ts>
struct typelist{};

struct mixin_tag{};

template <class T>
inline constexpr bool is_mixin = std::is_base_of_v<mixin_tag, T>;

template <std::size_t Idx, class Mxn>
struct mixin_base {
	Mxn& root(){ return *static_cast<Mxn*>(this); }
	const Mxn& root() const { return *static_cast<const Mxn*>(this); }
};

template <class Seq, class Mxn, class Core, template <class...> class... Frags>
struct mixin_impl;

template <std::size_t... Idx, class Mxn, class Core, template <class...> class... Frags>
struct mixin_impl<std::integer_sequence<std::size_t, Idx...>, Mxn, Core, Frags...>
	: Core, Frags<mixin_base<Idx, Mxn>>...
{
	using frags_list = typelist<Core, Frags<mixin_base<Idx, Mxn>>...>;
};

template <class Core, template <class...> class... Frags>
struct mixin : 
	mixin_impl
	< 
		std::make_index_sequence<sizeof...(Frags)>, 
		mixin<Core, Frags...>,
		Core,
		Frags...
	>, 
	private mixin_tag
{
};

template <class F>
void invoke_all_one(auto& mxn, auto&& fn){
	if constexpr ( requires  { fn(static_cast<F&>(mxn)); } )
		fn(static_cast<F&>(mxn));
}

template <class... Frags>
void invoke_all_impl(auto&& mxn, auto&& fn, typelist<Frags...>){
	(invoke_all_one<Frags>(mxn, fn), ...);
}

template <class Mxn, class Fn>
	requires is_mixin<std::decay_t<Mxn>>
void invoke_all(Mxn&& mxn, Fn&& fn){
    using list = typename std::decay_t<Mxn>::frags_list;
	invoke_all_impl(mxn, fn, list{});
}

以及使用案例

enum class state {
	happy,
	angry,
	sad, 
	asleep
};

template <state State>
struct tag{};
	
template <class R>
struct brain : R {
	
	template <class Tag>
	void set_state(Tag){
		constexpr auto impl = [] (auto& frag)
			requires requires { frag.change_state(Tag{}); }
			{ frag.change_state(Tag{}); };
		invoke_all(this->root(), impl);
	}
	
	enum state state;
};

template <class R>
struct mouth : R {
	void change_state(tag<state::angry>){
		std::cout << "SKREEEEEEEEEEEEEEEE\n";	
	}
};

template <class R>
struct claws : R {
	void change_state(tag<state::angry>){
		std::cout << "protracting claws...\n";
	}
};

template <class R>
struct tail : R {
	void change_state(tag<state::angry>){
		std::cout << "unfolding tail...\n";
	}
};


struct feet{};

using creature = mixin<feet, tail, claws, mouth, brain>;

int main(){
	creature c;
	c.set_state( tag<state::angry>{} );
}
/*
unfolding tail...
protracting claws...
SKREEEEEEEEEEEEEEEE
*/

作者文章写的不错,了解LLVM的可以看一看

介绍浮点数怎么比较,一般来说都是精度比较,给定一个精度,误差范围内决定大小,知乎这里也有个讨论值得一看

另外,这篇文章,英文介绍要更详细一些

这里有一系列讨论,英文

这里多说一下ULP这种方法,wiki看这里

代码

/* See
https://randomascii.wordpress.com/2012/01/11/tricks-with-the-floating-point-format/
for the potential portability problems with the union and bit-fields below.
*/

#include <stdint.h> // For int32_t, etc.

union Float_t
{
    Float_t(float num = 0.0f) : f(num) {}
    // Portable extraction of components.
    bool Negative() const { return i < 0; }
    int32_t RawMantissa() const { return i & ((1 << 23) - 1); }
    int32_t RawExponent() const { return (i >> 23) & 0xFF; }

    int32_t i;
    float f;
#ifdef _DEBUG
    struct
    {   // Bitfields for exploration. Do not use in production code.
        uint32_t mantissa : 23;
        uint32_t exponent : 8;
        uint32_t sign : 1;
    } parts;
#endif
};

bool AlmostEqualUlps(float A, float B, int maxUlpsDiff)
{
    Float_t uA(A);
    Float_t uB(B);

    // Different signs means they do not match.
    if (uA.Negative() != uB.Negative())
    {
        // Check for equality to make sure +0==-0
        if (A == B)
            return true;
        return false;
    }

    // Find the difference in ULPs.
    int ulpsDiff = abs(uA.i - uB.i);
    if (ulpsDiff <= maxUlpsDiff)
        return true;
    return false;
}

constexpr和vector可以结合使用

#include <vector>
#include <numeric>
#include <algorithm>

struct Point {
    float x, y;
    constexpr Point& operator+=(const Point& a) noexcept {
        x += a.x;
        y += a.y;
        return *this;        
    }
};

constexpr bool testVector(int n) {
    std::vector<Point*> vec(n);
    for (auto& pt : vec) {
        pt = new Point;
        pt->x = 0.0f;
        pt->y = 1.0f;
    }
    Point sumPt { 0.0f, 0.0f};
    for (auto &pt : vec)
        sumPt += *pt;
    for (auto& pt : vec)
        delete pt;
    return static_cast<int>(sumPt.y) == n;
}

int main() {
	static_assert(testVector(10));
}

缺点,不能这么用

constexpr std::vector vec = compute();

分配内存不是constexpr的,所以后面要allocator来支持

用array绕过还是可以的

讲异步抽象的对象生命周期问题

讲std::osyncstream的

这段代码

int main()
{
   std::vector<std::jthread> threads;
   for (int i = 1; i <= 10; ++i)
   {
      threads.push_back(
         std::jthread([](const int id)
            {
               std::cout << "I am thread [" << id << "]" << '\n';
            }, i));
   }
}

打印可能是乱的,但这段代码不是

int main()
{
   std::vector<std::jthread> threads;
   auto worker = [](std::string text) { std::cout << text; };
   auto names = { "Alpha", "Beta", "Gamma", "Delta", "Epsilon" };
   using namespace std::string_literals;
   for (auto const& name : names)
      threads.push_back(std::jthread(worker, "Hello, "s + name + "!\n"));
}

首先std::cout是线程安全的(除非设置sync_with_stdio(false)) 问题出在哪里?operator<<

怎么保证operator<<的原子性呢?用std::osyncstream 我怎么感觉是为了填坑补充的东西。

怎么用我就不介绍了。我觉得这套东西,别用,也别搞多线程printf的需求,这种需求一般log库都会去实现,加个队列来搞

视频

讲auto的没啥说的

项目


本文永久链接

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