C++ 中文周刊 第93期

周刊项目地址

公众号

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

提交 issue


资讯

编译器信息最新动态推荐关注hellogcc公众号 2022-12-14 第180期

文章

struct unpacked {
  char a;  // size: 1b => size: 4b
  int  b;  // size: 4b => size: 4b
  char c;  // size: 1b => size: 4b
           //             ---------
           //             size: 12b
};

struct packed {
  char a;  // size: 1b => size: 4b
  char b;  // size: 1b => size: 4b
  int  c;  // size: 4b => size: 8b
           //             --------
           //             size: 8b
};

static_assert(12 == sizeof(unpacked));
static_assert(8 == sizeof(packed));

struct simple {
  int a;   // size: 4b => align: 4b
};

struct empty {
           // size: 1b => align: 1b
};

这个我感觉都知道,问题是,如何检测是否对齐呢?

template<auto Id>
struct alignment {
  std::size_t* alignments{};
  template<class T> constexpr operator T() const {
    alignments[Id] = alignof(T);
    return {};
  }
};

template<class T, class... TArgs>
using AggregateInitializable = decltype(T{std::declval<TArgs>()...});

template<class T, auto... Ns>
constexpr auto is_packed_layout(std::index_sequence<Ns...>) {
  if constexpr(std::experimental::is_detected_v<AggregateInitializable, T, alignment<Ns>...>) {
    std::size_t alignments[sizeof...(Ns)]{};
    void(T{alignment<Ns>{alignments}...});
    return (alignments[Ns] <= ... <= sizeof(T));
  } else {
    return is_packed_layout<T>(std::make_index_sequence<sizeof...(Ns) - 1>{});
  }
}

template<class T, class = std::enable_if_t<std::is_aggregate_v<T>>>
constexpr std::bool_constant<is_packed_layout<T>(
  std::make_index_sequence<sizeof(T)>{})> is_packed_layout_v{};

static_assert(12 == sizeof(unpacked));
static_assert(not is_packed_layout_v<unpacked>);

static_assert(8 == sizeof(packed));
static_assert(is_packed_layout_v<packed>);

static_assert(1 == sizeof(empty));
static_assert(is_packed_layout_v<empty>);

static_assert(4 == sizeof(simple));
static_assert(is_packed_layout_v<simple>);

学习一波

值得一看

很精彩的抓bug,值得一看

很详尽的记录了c23变了啥

整了个脚本把moc开头的生成的头文件include一下

老生常谈了属于是,列表初始化这个玩意

一个cmake教程

直接看代码

auto [_, was_inserted] =
      done_events_.insert({device_ordinal, std::move(done_event)});



auto [installer_download_url, installer_filename] = extract_installer_asset_download_info(release_object);
co_return new_version_download_info{ extract_release_page_url(release_object),
                                                 std::move(github_version),
                                                 std::move(installer_download_url),
                                                 std::move(installer_filename) };



const auto [colorForeground, colorBackground] = renderSettings.GetAttributeColors(textAttributes);



const auto [first_nonmatching, error_condition] 
    = std::from_chars(val.data(), val.data() + val.size(), result);

其实就可以当作多个返回值解包来用

简单题,以下四段代码会不会inline?

A

inline int fn(int v) { return v; }
int main(int argc, char *argv[]) { return fn(0); }

B

//remove inline
int fn(int v) { return v; }
int main(int argc, char *argv[]) { return fn(0); }

C

//move function body
int fn(int v);
int main(int argc, char *argv[]) { return fn(0); }
int fn(int v) { return v; }

D

//extern C and non zero param
extern "C" int fn(int v);
int main(int argc, char *argv[]) { return fn(123); }
int fn(int v) { return v*0; }

答案是全都会inline, gcc/clang无差别。这是简单题。继续, 下面几段代码,头文件分开,代码是

#include "header.h"
int main(int argc, char *argv[]) { return fn(123); }

他们会不会inline?

A:

//Using a header without inline keyword
int fn(int v) { return v*0; }

B:

//using noinline
__attribute__ ((noinline))
int fn(int v) { return v*0; }

C:

//add the inline keyword
__attribute__ ((noinline))
__inline int fn(int v) { return v*0; }

D:

//Using both noinline and always_inline
__attribute__ ((noinline))
__attribute__((always_inline))
__inline int fn(int v) { return v*0; }

E:

//Same but swap
__attribute__((always_inline))
__attribute__ ((noinline))
__inline int fn(int v) { return v*0; }

有点复杂了

A clang/gcc都inline,

B clang会忽视这个noinline设置,直接inline,gcc不会

C 都不inline,因为指定了__inline又修饰成noinline,编译器准确翻译

D 都不inline,gcc会告警两个修饰词冲突

E 都inline gcc会告警两个修饰词冲突

DE都是修饰符出现的早说了算。另外CD如果去掉__inline那就都会inline了,得有inline,修饰才有作用

上强度了是吧?再来!

A:

#include <stdlib.h>
int main(int argc, char *argv[]) { return atoi("0"); }

B:

//using strtol
#include <stdlib.h>
int main(int argc, char *argv[]) { return strtol("0", 0, 10); }

C:

//using strtod which returns a double
#include <stdlib.h>
int main(int argc, char *argv[]) { return strtod("0", 0); }

谁会内联?这个涉及到这几个库接口的实现在那一层以及实现形式

atoi clang就能内联,而gcc是通过strtol来实现的

换个口味!库实现谁知道啊!下一题!

A:

#include <vector>
int main(int argc, char *argv[]) { std::vector<int> v; v.push_back(1000); return 0; }

B:

//Added an if
#include <vector>
int main(int argc, char *argv[]) { std::vector<int> v; v.push_back(1000); if (v.size() > 0) { return 0; } return 1; }

C:

//Added a pop inside the if and returned the size
#include <vector>
int main(int argc, char *argv[]) { std::vector<int> v; v.push_back(1000); if (v.size() > 0) { v.pop_back(); return v.size(); } return 1; }

会不会内联? clang能完全优化掉,gcc不能 (O2)。原因没深究。clang收益有点明显了,来点复杂的!

A:

class B { public: virtual int test() { return 0; }};
class D : public B { public: int test() override { return 1; }};
int main(int argc, char *argv[]) {
    D d;
    B*b = &d;
    auto p = dynamic_cast<D*>(b);
    return !p;
}

B:

//Add final to D
class B { public: virtual int test() { return 0; }};
class D final : public B { public: int test() override { return 1; }};
int main(int argc, char *argv[]) {
	D d;
	B*b = &d;
	auto p = dynamic_cast<D*>(b);
	return !p;
}

C:

//Both branches are 0
class B { public: virtual int test() { return 0; }};
class D final : public B { public: int test() override { return 1; }};
int main(int argc, char *argv[]) {
	D d;
	B*b = &d;
	auto p = dynamic_cast<D*>(b);
	if (p)
		return 0;
	return 0;
}

D:

//replace the first return
class B { public: virtual int test() { return 0; }};
class D final : public B { public: int test() override { return 1; }};
int main(int argc, char *argv[]) {
	D d;
	B*b = &d;
	auto p = dynamic_cast<D*>(b);
	if (p)
		return ((D*)p)->test()-1;
	return 0;
}

E:

//What happens if you change the cast to reinterpret_cast
class B { public: virtual int test() { return 0; }};
class D final : public B { public: int test() override { return 1; }};
int main(int argc, char *argv[]) {
	D d;
	B*b = &d;
	auto p = reinterpret_cast<D*>(b);
	if (p)
		return ((D*)p)->test()-1;
	return 0;
}

AB都内联不了。CD gcc能内联,E都能内联

已经有点折磨了,有啥差别?为啥E可以AB就不行?差别在哪里?为啥CD和AB差不多,但能内联?(坑爹的cast)

最后一轮! A:

//main.cpp
inline int getzero();
int main(int argc, char *argv[]) { return getzero(); }

//getzero.cpp
int getzero() { return 0; }

B:

//A again but add in -flto

C:

//Use -flto
#include <stdlib.h>
int main(int argc, char *argv[]) { return strtol("0", 0, 10); }

D:

//For a laugh, recursive fibonacci
int fibonacci(int n) {
	if(n == 0)
		return 0;
	else if(n == 1)
		return 1;
	else {
		return (fibonacci(n-1) + fibonacci(n-2));
	}
}
int main() { return fibonacci(0); }

A 不内联,会告警,B会内联,LTO发威,C即使LTO gcc也不内联,这是和实现有关的。clang可以,D是搞笑的就不说了

博士实现了一个strstr avx版本,代码在这里 https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2022/12/15/check.cpp

介绍c++23的属性,assume,这玩意是__builtin_assume

`void limiter(float* data, size_t size) {
    [[assume(size > 0)]];
    [[assume(size % 32 == 0)]];
    
    for (size_t i = 0; i < size; ++i) {
        [[assume(std::isfinite(data[i])]];
        data[i] = std::clamp(data[i], -1.0f, 1.0f);
    }
}

直接贴代码

bool exchange_unless(int value, int bad_value)
{
    int old_value = var.load(std::memory_order_acquire);
    do {
        if (old_value == bad_value) return false;
    while (!var.compare_exchange_weak(value, old_value,
                std::memory_order_release));
    return true;
}

另外,我不懂winrt Raymond chen的博客我就列下来,不多赘述了

视频

  1. 基本的单测/CICD
  2. clang-tidy配置
  3. sanitizer配置

开源项目需要人手

开源项目介绍


本文永久链接

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