编程语言学习指南

编程语言学习指南

组织

文件

数据类型

类型

数字

字符串

布尔

限定

常量

变量

数据结构

数组

列表

集合

映射

语法

程序结构

注释

运算符

条件控制

循环

判断

函数

特性

语法糖

空指针处理

函数式编程

泛型编程

kotlin 基础

安装

homebrew

1
$ brew install kotlin

下载

这里找最新的Release,贴一个v1.4.21链接

交互式终端

1
$ kotlinc-jvm

HELLO WORLD

code

1
2
3
4
// file: hello.kt
fun main() {
println("Hello Wrold!")
}

compile

1
$ kotlinc hello.kt -include-runtime -d hello.jar

run

1
$ java -jar hello.jar

组织

文件

kotlin 源文件通常以.kt 作为扩展名。

1
2
3
package base

import kotlin.text.*

数据类型

类型

数字

类型 长度
Double 64
Float 32
Long 64
Int 32
Short 16
Byte 8

字符

类型 长度
Char 字符
String -

布尔

类型 长度
Boolean -

限定

修饰符 意图 目标 注意
var 变量 属性
val 不可变量 属性
companion object 静态 -
final 不可继承,默认值 类、属性、方法 不可修饰局部变量
open 开放继承权限 类、属性、方法 不可修饰局部变量
const 修饰可执行 宏替换 的常量 属性 只能修饰不可变量(val定义)

注:

  • 宏替换
    • 宏变量 指编译时常量,宏替换指在编译阶段就会被替换掉
    • 宏替换条件
      • 使用 const 和 val 修饰的属性,如果进行拼接或者运算,被拼接的其他变量也要是宏变量
      • 位于顶层或者是对象表达式的成员
      • 初始值是基本数据类型
      • 没有自定义的getter方法

常量

1
2
3
4
5
6
7
8
9
10
11
// 10 进制 
1234567890
1234567890L
1_234_567_890

// 16 进制
0x0f
0xFF_EC_DE_5E

// 2 进制
0b00001011

定义

1
2
3
4
5
6
7
8
9
10
11
[final/open] [var/val] name : [Type] = ...
[final/open] [var/val] name : [Type]? = null
const val name : [Type][?] = ...

// example
var it : Int = 100
var ns : String? = null

object TestVars {
const val name : String = ""
}

数据结构

数组

列表

集合

映射

语法

程序结构

注释

1
2
3
4
5
6
// 这是单行注释

/*
这是
多行注释
*/

条件控制

运算符

循环

判断

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun [funName](arga: [Type], argb: [Type], ...) : [Type] {
return ...
}

// example
fun sum(a: Int, b: Int): Int {
return a + b
}

// no body
fun sum(a: Int, b: Int) = a + b

// return no meaning value
fun printSum(a: Int, b: Int): Unit { // Unit 可省略
println("sum of $a and $b is ${a + b}")
}

特性

语法糖

空指针处理

函数式编程

泛型编程

参考

安卓基础

生命周期

状态

  • INITIALIZED
  • CREATED
  • STARTED
  • RESUMED
  • DESTROYED

HOOK

  • onCreate()
  • onStart()
  • onResume()
  • onPause()
  • onStop()
  • onDestroy()
  • onRestart()

启动模式

  • standard
  • singleTop
    • 阻止创建栈顶Activity
  • singleTask
    • 阻止常见重复Activity
  • singleInstance
    • 使用独立栈

brpc

编译

1
2
3
4
5
$ bash config_brpc.sh --headers="/path/to/deps/include/" --libs="/path/to/deps/lib/" --with-glog --with-thrift
$ make -j ${make_thread_num}
$ cp -a output/include/* "/path/to/install/include/"
$ cp -a output/lib/* "/path/to/install/lib/"
$ cp -a output/bin/* "/path/to/install/lib/"

问题

NO 1

描述

1
2
3
4
5
6
ERROR: something wrong with flag 'flagfile' in file '/home/wii/Git/cooking/c++/universal/third/build/gflags-2.2.2/src/gflags.cc'.  One possibility: file '/home/wii/Git/cooking/c++/universal/third/build/gflags-2.2.2/src/gflags.cc' is being linked both statically and dynamically into this executable.

或者

Ignoring RegisterValidateFunction() for flag pointer 0x7f3d0b54893c: no flag found at that address
...

解决

gflags静态库和共享库同时存在导致。gflags 是选择手动编译,在编译时为cmake指定-DBUILD_SHARED_LIBS=1只编译共享库,如果是使用命令安装,可以尝试先移除/usr/local/lib/libgflag.a/usr/local/lib/libgflag_nothread.a

gflags

编译

1
2
3
4
$ mkdir build && cd build
$ cmake -DCMAKE_INSTALL_PREFIX:PATH="/path/to/install" -DBUILD_SHARED_LIBS=1 .. # 编译共享库,如需打包静态库 添加 -DBUILD_STATIC_LIBS=1;默认打包静态库,指定打包共享库不打包静态库,可同时指定
# -DGFLAGS_NAMESPACE=google 指定 namespace 名称,默认gflags
$ make -j4 && make install

更多参考这里

CMake

提取Namespace

1
2
3
4
5
6
7
8
9
10
11
12
execute_process(
COMMAND bash -c "grep \"namespace [_A-Za-z0-9]\\+ {\" ${DEPS_INCLUDE_DIR}/gflags/gflags_declare.h | head -1 | awk '{print $2}' | tr -d '\n'"
OUTPUT_VARIABLE GFLAGS_NS
)

if (${GFLAGS_NS} STREQUAL "GFLAGS_NAMESPACE")
execute_process(
COMMAND bash -c "grep \"#define GFLAGS_NAMESPACE [_A-Za-z0-9]\\+\" ${DEPS_INCLUDE_DIR}/gflags/gflags_declare.h | head -1 | awk '{print $3}' | tr -d '\n'"
OUTPUT_VARIABLE GFLAGS_NS
)
endif ()
message("gflags namespace: " ${GFLAGS_NS})

版本信息

CMakeLists.txt 添加版本定义

1
2
3
4
5
6
7
8
9
execute_process(
COMMAND sh -c "git describe --exact-match --tags 2> /dev/null || git rev-parse --short HEAD"
OUTPUT_VARIABLE TAG_REVERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (TAG_REVERSION STREQUAL "")
set(TAG_REVERSION "UNKNOWN")
endif ()
add_definitions(-DTAG_REVERSION="${TAG_REVERSION}")

设置版本信息

1
2
3
4
int main(int argc, char *argv[]) {
google::SetVersionString(TAG_REVERSION); // 在比较早的位置设置
...
}

二进制程序打印版本信息

1
./bin/{app} --version

使用

类型

基于gflags的设计哲学,不提供复杂的数据类型,可自行解析。

  • DEFINE_bool
  • DEFINE_int32
  • DEFINE_int64
  • DEFINE_uint64
  • DEFINE_double
  • DEFINE_string

定义

示例

1
2
3
4
5
#include <gflags/gflags.h>

DEFINE_bool(big_menu, true, "Include 'advanced' options in the menu listing");
DEFINE_string(languages, "english,french,german",
"comma-separated list of languages to offer in the 'lang' menu");

说明

可在任意文件中定义flag,但只能定义一次(只能 DEFINE 一次,但是可 DECLARE 多次)。如果需要访问位于多个源文件中的glfag,那么在其他文件中使用 DECLARE。最好,在foo.cc 中使用 DEFINE 定义,在foo.h 中使用 DECLARE 声明,那么只要在代码中添加 #include <foo.h> 就可以使用该flag。

访问

1
2
3
4
5
6
7
// 修改
FLAGS_languages += ",klingon";

// 方法调用
if (FLAGS_languages.find("finnish") != string::npos) {
// do something
}

声明检查

1
2
3
4
5
6
7
8
static bool ValidatePort(const char* flagname, int32 value) {
if (value > 0 && value < 32768) // value is ok
return true;
printf("Invalid value for --%s: %d\n", flagname, (int)value);
return false;
}
DEFINE_int32(port, 0, "What port to listen on");
DEFINE_validator(port, &ValidatePort);

在全局初始化时(紧接着DEFINE_int32定义flag)注册检查方法,可以保证在运行main()函数解析命令号之前执行校验。上面的代码,使用DEFINE_validator 宏指令调用 RegisterFlagValidator 方法,如果注册成功,返回true;如果第一个参数不是命令行标记或者该标记注册了其他校验,则返回false;返回值以<flag>_validator_registered 命名作为全局变量供访问。

解析标记

1
gflags::ParseCommandLineFlags(&argc, &argv, true);

通常,改代码唯有main()的开始。argcargv 是传入main() 方法的准确参数,该例程可能会修改他们。最后的布尔参数用于指示是否从argc中删除标记(flags)以及其参数(arguments),并同步修改argv。如果为true,在该方法调用之后,argv只会保存命令行参数(commandline arguments),而不是命令行标记(glags)。如果为false,那么 ParseCommandLineFlags 不会修改 argc,但是会对argv中的参数重排序,使得所有的标记在最开始。比如,"/bin/foo" "arg1" "-q" "arg2",方法会重排序 argv 为 "/bin/foo" "-q" "arg1" "arg2",ParseCommandLineFlags 返回 argv 中第一个参数的索引,也即标记(flag)的后面的索引(在刚才的例子中,返回2,因为 argv[2] 指向第一个参数 arg1)。

,命令行标记(commandline glags)形如 -e -q,命令行参数(commandline arguments)形如 arg1 arg2

在命令行设置标记

1
$ foo --nobig_menu -languages="chinese,japanese,korean" ...

当运行 ParseCommandLineFlags 时,会设置 FLAGS_big_menu = false; FLAGS_languages = "chinese,japanese,korean"。通过在标记前面添加no,为标记设置为 false 值。以下方式均可设置 languages 值。

1
2
3
4
app_containing_foo --languages="chinese,japanese,korean"
app_containing_foo -languages="chinese,japanese,korean"
app_containing_foo --languages "chinese,japanese,korean"
app_containing_foo -languages "chinese,japanese,korean"

布尔型值稍有不同。

1
2
3
4
app_containing_foo --big_menu
app_containing_foo --nobig_menu
app_containing_foo --big_menu=true
app_containing_foo --big_menu=false

尽管有很多方式灵活设置标记值,但是推荐使用统一的格式 --variable=value,对于布尔值使用 --variable/--novariable

注意

  • 如果在命令行中指定了标记,但是没有在程序中定义(使用DEFINE),将会导致致命错误。可以通过添加 --undefok 来屏蔽该异常
  • 在 getopt 中,-- 标记会终止标记解析,所以 foo -f1 1 -- -f2 2,中,f1 是标记,f2 不是
  • 如果标记被指定多次,后面的值覆盖前面的,即最后一次定义有效
  • 不像 getopt 库那样支持 单字母同义词,比如 -h (和 --help 同义),同样的也不支持组合标记,如 ls -la

修改默认值

有时候标记位于依赖库中,如果只想在一个程序中修改默认值,其他应用不变。可在 main() 方法中赋值新值,在调用 ParseCommandLineFlags 之前。

1
2
3
4
5
DECLARE_bool(lib_verbose);   // mylib has a lib_verbose flag, default is false
int main(int argc, char** argv) {
FLAGS_lib_verbose = true; // in my app, I want a verbose lib by default
ParseCommandLineFlags(...);
}

这种情况下,用户依然可以在命令行中指定标记值,但是如果不指定,则使用新的默认值。

特殊标记

  • --undefok=flagname,flagname,...

    • 对于指定的这些标记名称,用于阻止当命令行指定标记但在程序中未定义导致的异常退出
  • --fromenv

    • --fromenv=foo,bar

    • 从环境变量中读取值,如果环境变量中未定义,则会导致致命异常

    1
    2
    3
    4
    5
    6
    export FLAGS_foo=xxx; export FLAGS_bar=yyy   # sh
    setenv FLAGS_foo xxx; setenv FLAGS_bar yyy # tcsh

    # 等同于

    --foo=xxx, --bar=yyy
  • --tryfromenv

    • 类似于 --fromenv ,区别是如果环境变量中未定义标记,不会导致异常
  • --flagfile

    • --flagfile=f

    • 从文件读取参数定义,文件内容为标记定义列表,每行一个

    • 与命令行不同的是,需要等号将标记和参数值分开(命令行参数有多种设置方式,可不加等号)

    1
    2
    3
    4
    5
    6
    7
      # 示例文件 /tmp/myflags:
    --nobig_menus
    --languages=english,french

    # 两种等价方式
    ./myapp --foo --nobig_menus --languages=english,french --bar # --foo 和 --bar 泛指其他参数
    ./myapp --foo --flagfile=/tmp/myflags --bar
    • 注意:flagfiles方式,一些错误会默认禁止。特别的,未识别的标记名异常默认忽略(这在命令行设置方式下,需要显式使用--undefok 指定),以及未指定值的异常(比如在flagfile中只定义 --languages ,而不指定参数)

    • 通常的 flagfile 比示例要复杂:一系列文件名,每行一个,每个后面紧接着一系列标记,每行一个,根据需要重复多次。flagfile中的文件名可用通配符,比如 *?,仅当当前程序的名称和flagfile文件中filenames中的一个匹配时,才会处理其挨着的标记

    • # 开头的行会被忽略

    • 空白行及前置空白符会被忽略

    • 可在 flagfile 中使用 --flagfile 指定其他 flagfile

    • 标记总是以期望的方式处理,首先从检查命令行参数开始,如果遇到 flagfile ,则处理其内容,然后继续处理后续标记

1
2
3
4
5
6
7
8
9
10
11
# 示例文件 gflags_test.cmd
gflags-test-foo
--port=10000
--service_name=gflags_test_foo
gflags-test-bar
--port=20000
--service_name=gflags_test_bar

# 如果程序名称是 gflags-test-foo,则 --port=10000 --service_name=gflags_test_foo
# 如果程序名称是 gflags-test-bar,则 --port=20000 --service_name=gflags_test_bar
# 实例程序参考这里 https://github.com/sunzhenkai/cooking/blob/master/c%2B%2B/universal/scripts/gflags_test.sh

其他

移除帮助信息

可减少编译源文件时的帮助信息,及二进制文件大小,或一些安全相关的隐患。

1
2
#define STRIP_FLAG_HELP 1    // this must go before the #include!
#include <gflags/gflags.h>

问题

NO 1

描述

1
ERROR: something wrong with flag 'flagfile' in file '/home/wii/Git/cooking/c++/universal/third/build/gflags-2.2.2/src/gflags.cc'.  One possibility: file '/home/wii/Git/cooking/c++/universal/third/build/gflags-2.2.2/src/gflags.cc' is being linked both statically and dynamically into this executable.

解决

在编译gflags时,使用 cmake 参数 -DBUILD_SHARED_LIBS=1 指定只编译共享库。

参考

2021 todo list

事项

  • 早睡早起,每天12点前睡觉,保持七个小时睡眠
  • 每天2L水
  • 徒步(10公里+) / 骑行(20公里+)8次
  • [ ] 体重控制在140以下
  • 读25本书,8本技术书
  • 上线博客站点,文章 50+
  • 掌握特征工程及模型训练
  • 精读一种训练框架源码(tensorflow、xdl、paddlepaddle)
  • 熟读SpringMVC源码
  • 熟读一种微服务框架(dubbo、Spring Cloud)
  • 熟读一种DB源码(RocksDB、Redis、HBase)

读书

文学

  • [ ] 百年孤独
  • [x] 人生海海

经济学

  • [ ] 贫穷的本质
  • [ ] 经济学原理
  • [ ] 结构性改革

心理学

  • [x] 被讨厌的勇气
  • [ ] 反脆弱

传记

  • [x] 只有偏执狂才能生存
  • [ ] 忏悔录

互联网

  • [ ] 浪潮之巅

技术

  • [x] 设计模式(Head First)
  • [ ] 分布式服务架构原理、设计与实战

效率

  • [ ] 高效能人士的7个习惯

成长

  • [ ] 想哲学家一样生活
  • [ ] 沉思录

文章

算法

效率

cmake/makefile flags

规约

编译源文件

1
$(CC) $(CPPFLAGS) $(CFLAGS) example.c -c -o example.o # -c: 编译,不执行链接操作

链接

1
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) example.c -o example # -o: 指定输出文件名字

命令

命令 说明
CC C 编译器
CXX C++ 编译器
CPP C / C++ 预编译器,通常是 “$(CC) -E”

示例

1
2
export CC=/usr/local/bin/gcc-7
export CXX=/usr/local/bin/g++-7

变量

变量 含义 示例
CFLAGS C 编译器选项
CXXFLAGS C++ 编译器选项
CPPFLAGS C/C++ 预处理器的命令行参数
LDFLAGS 链接参数
LD_LIBRARY_PATH 运行时动态链接库查找路径
LIBRARY_PATH 编译时链接库查找路径 /usr/lib
C_INCLUDE_PATH 头文件查找路径
CPLUS_INCLUDE_PATH C++ 头文件查找路径
OBJC_INCLUDE_PATH ObjectiveC 头文件查找路径
CPATH C/C++/ObjectiveC 头文件默认查找路径,多个路径使用 : 分隔,比如 .:/root/include
DYLD_LIBRARY_PATH Mac OS 动态链接库查找路径

参数

参数 说明 示例
c 编译 -c
o 输出文件名称 -o
g 添加调试信息 -g
l(小写L) 链接标准库 -lz
L 指定库搜索路径 -L/user/local/lib
I(大写i) 指定头文件搜索路径 -I/user/local/include
static 在支持动态链接的系统中,该参数覆盖-pie,并阻止链接共享库 -static
shared 生成可被链接的共享对象 -shared
PIC / pic 使用位置无关代码创建对象文件,创建共享库时需指定 -fpic / -fPIC

说明

  • -llibrary / -l library 链接时,搜索指定库,优先使用共享库,除非指定 -static 参数

cmake 使用

示例

CMakeLists.txt

1
2
3
$ mkdir cmake-project
$ cd cmake-project
$ touch CMakeLists.txt

Code

1
$ mkdir src && vim src/main.cpp

Config

1
2
3
4
5
6
7
cmake_minimum_required(VERSION 3.10)

# set the project name
project(Tutorial)

# add the executable
add_executable(Tutorial src/main.cpp)

Build

1
2
3
$ mkdir build && cd build
$ cmake ..
$ make

Run

1
2
$ ./Tutorial
Hello, CMake.

语法示例

1
2
3
cmake_minimum_required(VERSION 2.8)
project("pybindcpp")
add_executable(HELLO src/pybindcpp.cpp)

完整示例

1
2
3
4
cmake_minimum_required(VERSION 2.8)
project("pybindcpp")

add_executable(HELLO src/pybindcpp.cpp)

编译

1
2
3
$ mkdir build && cd build
$ cmake ..
$ make

macro

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
macro(AddLibrary MODULE)
set(options NONE)
set(oneValueArgs PREFIX DEP)
set(multiValueArgs SUBMODULES)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
message(STATUS "AddLibrary MODULE=${MODULE} PREFIX=${ARG_PREFIX} DEP=${ARG_DEP} SUBMODULES=${ARG_SUBMODULES}")


if ("${ARG_PREFIX}" STREQUAL "")
message(FATAL_ERROR "PREFIX should not be empty")
endif ()
foreach (I IN LISTS ARG_SUBMODULES)
set(TGT ${MODULE}::${I})
add_library(${TGT} STATIC IMPORTED GLOBAL)
set_target_properties(${TGT} PROPERTIES
IMPORTED_LOCATION "${ARG_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}${I}${CMAKE_STATIC_LIBRARY_SUFFIX}"
INCLUDE_DIRECTORIES ${ARG_PREFIX}/include)
add_dependencies(${TGT} ${ARG_DEP})
endforeach ()
endmacro(AddLibrary)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
macro(AddLibraryV2 MODULE)
set(options NONE)
set(oneValueArgs PREFIX DEP)
set(multiValueArgs SUBMODULES)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
message(STATUS "AddLibrary MODULE=${MODULE} PREFIX=${ARG_PREFIX} DEP=${ARG_DEP} SUBMODULES=${ARG_SUBMODULES}")


if ("${ARG_PREFIX}" STREQUAL "")
message(FATAL_ERROR "PREFIX should not be empty")
endif ()
foreach (I IN LISTS ARG_SUBMODULES)
find_path(TGT_LIB_${I} NAMES "${I}" "lib${I}" HINTS ${ARG_PREFIX} PATH_SUFFIXES lib lib64)
find_path(TGT_INCLUDE_${I} NAMES "${MODULE}" HINTS ${ARG_PREFIX} PATH_SUFFIXES include)
set(TGT ${MODULE}::${I})
message(STATUS "AddLibrary TARGET=${TGT} TARGET_LIB_DIR=${TGT_LIB_${I}} TARGET_INCLUDE_DIR=${TGT_INCLUDE_${I}}")
add_library(${TGT} STATIC IMPORTED GLOBAL)
set_target_properties(${TGT} PROPERTIES
IMPORTED_LOCATION "${TGT_LIB_${I}}/${CMAKE_STATIC_LIBRARY_PREFIX}${I}${CMAKE_STATIC_LIBRARY_SUFFIX}"
INCLUDE_DIRECTORIES ${TGT_INCLUDE_${I}})
add_dependencies(${TGT} ${ARG_DEP})
include_directories(${TGT_INCLUDE_${I}})
endforeach ()
endmacro(AddLibraryV2)