0%

现在的构建工具有很多,CMake是其中的佼佼者,虽然很多人说CMake编译速度太慢,但架不住兼容性好。

我了解的构建工具有bazel、ninja等,但暂时都没用过,先掌握CMake吧。

从一个简单的项目开始

一个最简单的例子

1
2
3
cmake_minimum_required(VERSION 3.0)
project(myApp)
add_executable(myTarget sample.cpp)

只有3行,但也构建了CMakeLists.txt的骨架。

  • 指定cmake支持的最小版本
  • 指定项目名称
  • 指定目标名称及其依赖

注意事项

  1. 关于targetname和projectname
    targetname和projectname不要相同,projectname直接在CMakeLists.txt中指定,

那么如果我们要依赖库呢

我们先添加一个预先生成好的库,CMakeLists.txt更新为

1
2
3
4
cmake_minimum_required(VERSION 3.0)
project(myApp)
add_executable(myTarget sample.cpp)
target_link_libraries(myTarget PUBLIC ui PRIVATE algorithm INTERNAL framework)

本文用来分析Epic提供的MetaHumans工程架构和业务逻辑。

之前用hexo搭建过blog,当时也新建了一个github repo用来备份,但不知道怎么恢复,因为是在虚拟机里搭的环境,后来也丢了,现在重新整理一遍,最重要的是备份,换台机器照样能搞定。

原理

hexo依赖于node,环境是不需要备份的,那么我们要备份的是除了环境和包之外的东西。
我们的github.io repo上有两个分支,其中master用来发布,hexo用来备份。
注意:发布的东西和备份的东西是有差别的,备份的是源文件,发布的是hexo生成后的文件。

我们的操作步骤是

  1. 在github.io hexo分支备份源文件
  2. 本地文件夹blog负责发布master分支所需要的生成后文件

我们可以每次修改后,把blog里修改的文件拷贝到github.io/hexo下面,然后git push。但这样太过麻烦了,我们可以把blog中我们需要的文件git add,作为hexo分支所需要的文件,然后在同一个文件夹内,保持两个branch的内容。

Read more »

为什么要学习这本书?

Linux C++ 服务器端这条线怎么走?一年半能做出什么? - 陈硕的回答 - 知乎

如何阅读《深入理解计算机系统》这本书? - whereisKathy的回答 - 知乎

MIT6.828 - Operating System Engineering - 操作系统公开课 - whereisKathy的文章 - 知乎

CS15213 参考资料
CMU 15213/15513 CSAPP 深入理解计算机系统 Lecture 01 Course Overview 中英字幕
CS15213 官网

MIT 6.828 参考资料
Learning Material
labs
xv6

CSAPP

学习路线是什么

先学习《深入理解计算机系统》,然后学习《操作系统》。

日程安排

6.30日之前尽可能完成,因为过了这天之后有重要的事情要发生。

学习方法

看国外公开课,看国外教材,做课后作业,使用Google,多思考,多动手,多讨论。

直到今天才发现,过河摸TM什么石头啊,高速公路都建好了,开车过去就行了,少自欺欺人。

课程总览

Chapter 2 信息的表示与存储

Q1 我们都知道CPU处理的是补码,那么它是怎么知道结果是正还是负的?另外不是还有一些指令是区分正负的吗?标志寄存器里存储的是结果的正负值吗?

3 万字 51 张图教你 CPU、内存、操作系统硬核知识!
上面这个链接里的文章当个科普就好,最起码里面关于标志寄存器的介绍就是错误的,标志寄存器里不存储数据是正数 负数。标志寄存器

我目前的理解是:

  1. 在程序中读取了一个变量,假设是有符号int类型,系统或者解释器会先翻译成补码
  2. 运算之后的结果也是补码。如果一个signed和unsigned运算,二者会先有一个规则,就是统一按照unsigned还是signed来转换成补码,然后再运算,也就是说CPU在拿到数据的时候,只知道这是补码,至于类型什么的都是在传给CPU之前就处理好了。
  3. 结果是补码,如果我们想把结果赋值给一个变量(假设这个变量是个栈中的临时变量),此时会把补码按照最终要赋值给的类型来做转换。

Q2 char数据左移8位的结果为什么有可能不是0?

在这里我曾经百思不得其解,但现在我突然意识到一个问题,那就是虽然C++是高级语言,但仍要了解底层是如何实现的,因为在可 正常 范围内,抽象层和底层的预期一致,我们不用关心

1
2
3
4
char* p = new char[1024];
delete p;

cout << p[0] << endl;

上面这段代码会崩溃吗?

不一定,delete是释放堆空间,但实际上内存只是被回收了,p还是指向那块区域,所以我们还可以继续使用,但这块空间的value有可能已经不是我们原来的value了,因为如果这块空间被重新分配给其它变量的话,有可能就会读写这块空间,value就会被改变。

To be continued

在看VS版本的thread库源码时,发现了这么一段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// STRUCT TEMPLATE enable_if
template <bool _Test, class _Ty = void>
struct enable_if {}; // no member "type" when !_Test

template <class _Ty>
struct enable_if<true, _Ty> { // type is _Ty for _Test
using type = _Ty;
};

template <bool _Test, class _Ty = void>
using enable_if_t = typename enable_if<_Test, _Ty>::type;



class thread { // class for observing and managing threads

...

public:
template <class _Fn, class... _Args, enable_if_t<!is_same_v<_Remove_cvref_t<_Fn>, thread>, int> = 0>
explicit thread(_Fn&& _Fx, _Args&&... _Ax) {
using _Tuple = tuple<decay_t<_Fn>, decay_t<_Args>...>;
auto _Decay_copied = _STD make_unique<_Tuple>(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
constexpr auto _Invoker_proc = _Get_invoke<_Tuple>(make_index_sequence<1 + sizeof...(_Args)>{});

#pragma warning(push)
#pragma warning(disable : 5039) // pointer or reference to potentially throwing function passed to
// extern C function under -EHc. Undefined behavior may occur
// if this function throws an exception. (/Wall)
_Thr._Hnd =
reinterpret_cast<void*>(_CSTD _beginthreadex(nullptr, 0, _Invoker_proc, _Decay_copied.get(), 0, &_Thr._Id));
#pragma warning(pop)

if (_Thr._Hnd) { // ownership transferred to the thread
(void) _Decay_copied.release();
} else { // failed to start thread
_Thr._Id = 0;
_Throw_Cpp_error(_RESOURCE_UNAVAILABLE_TRY_AGAIN);
}
}

...

}

在这里我们看到thread构造函数的模板参数有三个,Fn和不定参数模板Args,以后最后的enable_if_t__。

enable_if_t 是 enable_if在_Test为true的情况下 才会存在别名type,而此时type=第二个参数类型。

那么 _is_same_v<_Remove_cvref_t<_Fn>, thread> _必须为false

1
2
3
4
5
6
7
8

template <class _Ty>
using _Remove_cvref_t = remove_cv_t<remove_reference_t<_Ty>>;

template <class, class>
_INLINE_VAR constexpr bool is_same_v = false; // determine whether arguments are the same type
template <class _Ty>
_INLINE_VAR constexpr bool is_same_v<_Ty, _Ty> = true;

_Remove_cvref_t 是取出类型本身的引用(包括左值 右值)属性、const、volatile属性。

也就是说,对thread构造中enable_if_t中 _Fn和thread必须是不同类型,也就是_Fn不能是thread类型,那_Fn是其他任何类型都可以了? 不是的,必须是Callable&&类型,因为必须支持std::invoke,这些在其它博文中再探讨。

回到本文主题 enable_if_t的作用是什么,有没有其它方式实现,我们该在什么场合,如何使用它?

首先,我们要介绍一个概念 SFINAE

在函数模板的重载决议中应用此规则:当将模板形参替换为显式指定的类型或推导的类型失败时,从重载集中丢弃这个特化,而非导致编译失败。

此特性被用于模板元编程。

今天找到一个非常直观的git学习网站 https://learngitbranching.js.org/ 为啥需要学习git呢,本来以为自己已经学会了,结果发现在某种使用场景中,自己又找不到对策了,很多概念都弄不懂,感觉连“知其然”都做不到,更不要说“所以然”了。

Let’s go!

Read more »

如果让我设计一个测试框架,我会怎么弄?

我有几个疑问:

  • 是面向接口测试,还是面向功能测试?比如我要实现某个功能X,可能要调用接口A,接着B,最后C。当然可以采用test suite的概念,还是按照接口写test case,但一些test case可以组成test suite
  • test case能否做到与input output无关?比如A->B A->C , 这两个test suite,给A的输入不同,输出也不同,有人说那你test suite组合起来就好了,接口可以做到跟输入分离,可如果是单独测试A接口呢?有单独的input? 这倒是也可以。

这么一想,问题倒是都能解决,接口测试为基本的test case,接口与输入、输出分离,比如可以有config,不是写死的状态;根据功能性,某些test case可以组成test suite,这里面接口用到的input output也是不同的。

接下来我们看看Googletest的使用以及实现原理吧。

1 大体思路

gtest的github repository里有gtest和gmock,gmock可以认为是依托gtest实现了更高级的功能(现在还没看,后面写sample)。编译成功之后,会生成libgtest.a libgmock.a libgtest_main.a libgmock_main.a,这里libgtest_main.a 其实是包含了以下代码

int main(int argc, char* argv[])
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

这样我们写unit_test.cpp就可以了,然后直接编译进去,生成可执行,然后直接跑就会进入到libgtest_main.a的main函数里,跑所有的test。

2 正式集成

2.1 现有工程的TestSuite

今天看公司代码里的测试用例,全部是按照功能来写的,大概类似于

TEST(TestSuiteName, TestCaseName)
{
    Init();
    Start();
    Stop();
    Destroy();
}

这样按照功能来写TestCase是可以的,这样即使例子调用的接口完全相同,因为输入的不同也可以是不同的TestCase。

1 前言

编程语言是特性的合集,比如for while,很多语言都有,lambda表达式也是,语言也在不断发展,协程的概念出来之后,很多语言也就着手修订新的标准(特性或者标准库)。如果从这个角度来理解,作为一个开发者,我们所掌握的其实是 算法 + 架构 + 语言特性,算法是可以完全抽离的,架构从宏观的角度上看也可以抽象出来,但因为不同语言生态差异,同一种架构用不同语言开发的成本千差万别,比如后端业务逻辑我们用C++的成本远远大于Java。至于语言特性,作为一个大的set,每一种语言从中挑选若干特性组合起来。至于为什么每个语言的写法不同,但即使两种语言有完全相同的特性,因为底层机制的不同,性能和写法差异也很大,比如Python这种胶水语言就比C++写的方便多了,还有Java C#也明显比C++写起来快,它们底层的虚拟机屏蔽了系统的细节,比如内存管理。

下面开始正经的学习一下Python。

声明一下本篇为自己参考Python 100天 做的学习笔记。

Read more »

ubuntu 16.04上装了搜狗输入法,但每次调出来的时候,就是乱码

按两次shift就变成正常了

每次都特别麻烦,后来在网上找到原因了Ubuntu搜狗输入法乱码问题

因为我们在设置输入法的时候,把搜狗设置为首选输入法了,解决方案是把英文设置为首选输入法,感谢上面链接作者找到了解决方法。