XMake 速成笔记
虽然xmake的使用远没有cmake那么普及,cmake在可预见的将来都还是c++项目事实上的标准,
但是对于一些个人的小项目,直接使用xmake看起来还是更方便的,因此简单学习一下使用方式。
下载安装
虽然XMake支持了很多种安装方式,但是我还是选择了纯净版安装。
在Github仓库中选择xmake-[version]-win64.zip下载到本地解压,然后把对应路径添加到PATH环境变量即可。
Demo项目
xmake可以直接生成C++的HelloWorld项目
1xmake create -l c++ -P ./demo
这个命令会在当前位置下创建./demo文件夹,在其中自动生成如下文件结构
12345demo├── src│ └── main.cpp├── .gitignore└── xmake.lua
其中xmake的核心配置文件xmake.lua有效内容如下
12345add_rules("mode.debug", "mode.release")target("demo") set_kind(&quo ...
Lua 速成笔记——2. 进阶内容
元表 (Metatable)
元表(metatable)是 Lua
提供的一种机制,用于改变表(table)的行为。
通过对元表(metatable)的设置可以自定义表(table)的操作行为,如算术运算、索引等,甚至可以用来实现面向对象机制。
有两个涉及元表的操作函数:
使用setmetatable函数可以设置一个表的元表,第一个参数为目标的表,第二个参数为提供的元表,返回值是第一个参数,无论是否接收第一个参数,这里的修改都是有效的,因为传参过程相对于引用传递;
使用getmetatable函数可以获取一个表的元表,提供的参数就是目标的表。
运算符元方法
对于常见的运算符,有对应的元方法
__add 对应运算符 +
__sub 对应运算符 -
__mul 对应运算符 *
__div 对应运算符 /
__mod 对应运算符 %
__unm 对应运算符 -
__eq 对应运算符 ==
__lt 对应运算符 <
__le 对应运算符 <=
__concat 对应运算符 ..
例如用自定义的加法行为实现表的相加 1234567891011121314local t1 = ...
Lua 速成笔记——1. 基础内容
简单学一下Lua这个有点过时的轻量级脚本语言吧,因为很多工具(nvim、xmake、MySQL等)都采用了Lua脚本提供配置,
而且直到现在,在c++项目使用Lua脚本提供配置也是一个可以考虑的方案。
关于Lua的教程很多都是速成版的,因为内容实在比较简单,比如Learn Lua in Y
minutes。
编译安装
Lua是一种开源的脚本语言,完全使用C语言编写,Lua官网直接提供了源码,
在Linux系统中的下载和源码编译流程如下 1234curl -L -R -O https://www.lua.org/ftp/lua-5.4.7.tar.gztar zxf lua-5.4.7.tar.gzcd lua-5.4.7make all test
编译命令非常简单,编译完成后可以得到两个产物:lua和luac,两者仍然存放在src/文件夹中,将其移动到其它位置,
然后将路径添加到环境变量即可。其实编译产物中还有一个Lua库,用于嵌入到C语言项目中,但是暂时不需要。
由于Lua的源码编译过程本身非常简单,我们可以迁移到Windows上进行源码编译,为了方便还可以把Makefile改成CM ...
Cpp 智能指针
概述
C++标准库主要提供了三种智能指针:
shared_ptr:共享指针,允许资源共享,在内部维持一个引用计数,复制会增加引用计数,析构则会减少引用计数,引用计数为零则释放资源
unique_ptr:独享指针,独自负责资源的管理,析构时释放资源
weak_ptr:弱共享指针,作为共享指针的辅助手段,可以指向共享指针的内容但是不参与引用计数,主要为了解决共享指针的循环引用问题
除此之外,在早期还提供了auto_ptr,但是目前已经被废弃。下面先介绍RAII思想,然后介绍三种智能指针的使用。
简单示例与RAII
先给出使用原始指针和unique_ptr管理资源的对比示例
123456789101112131415{ int* p = new int(100); // ... delete p; // 必须记得手动释放内存}{ std::unique_ptr<int> up = std::make_unique<int>(200); //... // up 在析构时自动释放内存}
这里 ...
Cpp 多线程学习笔记——5. future异步编程
概述
C++通过<future>头文件提供了一组支持异步编程的工具,使用这些工具比直接进行多线程操作更加高级、更加简便。
主要包括如下的类型:
std::future:表示异步操作的结果,这个结果在未来可能可用,支持查询操作的状态,等待操作完成和获取结果。注意用于获取结果的get()方法调用会阻塞当前执行流,直到结果准备就绪。
std::promise:承诺在未来提供一个可用的值,通常与
std::future
配对使用,set_result()可以设置异步操作的结果。可用get_future()提取获得一个关联的std::future对象。
std::packaged_task:封装一个函数或可调用对象,使其可以作为异步任务执行。可用get_future()获得一个关联的std::future对象。
还包括如下的函数:
std::async:用于启动异步任务,返回一个std::future对象代表任务的结果,注意我们必须要用变量接收这个返回值,否则当前语句会阻塞式的等待任务结束,因为只有异步任务结束才会销毁返回的临时变量!
这里std::future和std:: ...
Cpp 多线程学习笔记——4. 条件变量
介绍
在C++中通过<condition_variable>头文件提供了条件变量类型std::condition_variable和std::condition_variable_any,
条件变量提供了一种线程间的同步机制:允许一个或多个线程等待某个条件变为真,同时另一个线程可以改变这个条件并通知等待的线程。
条件变量通常需要和同步锁结合使用。
std::condition_variable_any与std::condition_variable非常类似,
区别仅仅是std::condition_variable的wait函数只能接受std::unique_lock<std::mutex>类型的参数,
而std::condition_variable_any可以接受任何lockable参数,相应地需要付出额外开销。
除此以外,两者的使用几乎完全一样。
下文中只讨论std::condition_variable的使用,一般也不推荐使用std::condition_variable_any。
使用方法
std::condition_variable提供了几种方法来 ...
Cpp 多线程学习笔记——3. 原子操作
原子类型和原子操作
原子操作是指在多线程编程中对共享数据的操作是不可分割、不会被中断的操作,
这意味着操作不会被其他线程干扰,不会被调度切换,要么一次性执行完成,要么完全不执行,不存在第三种状态。
原子操作可以用于避免数据竞争和保证线程安全,不过显然我们需要为安全性付出额外的性能开销。
原子类型是一种特殊的数据类型,在底层保证对原子类型变量的相关操作是原子操作,
例如对原子类型变量的读取、写入、交换、递增、递减等。
这里我们再次重复前一篇使用的例子,只是改动了共享变量的定义:使用原子类型的整数变量shared_counter而非通常的整数变量
1234567891011121314151617181920212223242526272829303132333435363738#include <atomic>#include <iostream>#include <thread>#include <vector>const int num_threads = 5;const int num_increments = 10000;const ...
Cpp 多线程学习笔记——2. 互斥锁
线程安全与互斥锁
在多线程编程中,由于多个线程存在共享的资源(例如全局变量等),因此可能导致相互之间产生干扰,
下面的例子可以展示这种问题(必须使用Debug模式编译,因为Release模式下可能直接优化了)
12345678910111213141516171819202122232425262728293031323334353637#include <iostream>#include <thread>#include <vector>const int num_threads = 5;const int num_increments = 10000;const int num_experiments = 20;void increment_counter(int &counter) { for (int i = 0; i < num_increments; ++i) { counter++; // 多线程同时修改共享变量 }}void run_experimen ...
Cpp 多线程学习笔记——1. 线程
基础概念
首先学习几组概念:
并发(Concurrency)/并行(Parallelism)
并发指的是任务在同一时间段内被交替执行,不一定是同时执行(取决于CPU的核数和CPU的调度)
并行指的是任务在同一时间内真正同时执行,并行是并发的一种特例(并发是在不同的核上面同时执行的,因此对CPU有要求)
进程(Process)/线程(Thread)
进程是程序执行的实例,是系统分配资源的基本单位,拥有自己的代码区、堆区、栈区、全局变量等。进程之间相互独立,一个进程的崩溃不会影响其他进程,进程之间的通信开销较大,需要特殊的机制
线程是进程中的执行单元,是CPU调度中的基本单位。一个进程默认会有一个主线程,主线程对指令依次执行,也可以拥有多个线程,每一个线程独立执行指令(单独的执行流)。线程有自己独立的调用栈(因此函数内的局部变量是线程私有的),与其他线程共享所属的进程中的代码区、堆区和其他资源(全局变量是线程共有的),创建和切换线程,以及线程之间的通信比进程开销更小,但是存在数据的竞争等问题
同步(Synchronous)/异步(Asynchronous)
同步是指任务按照顺序 ...
C++ 设计模式笔记——7. 相关概念
除了设计模式,还有几个编程概念与之相生相随,尤其在Java中被广泛的应用:面向切面编程、控制反转和依赖注入。
下面简单学习一下这几个概念,并在C++/Python中尝试应用。
面向切面编程
面向切面编程(Aspect-Oriented Programming,
AOP)是一种编程范式,它旨在提高软件模块化,通过将横切关注点(Cross-cutting
Concerns)与业务逻辑分离来简化程序结构。
横切关注点是指那些分散在多个模块中的功能,如日志记录、事务管理、权限控制等,这些功能虽然对多个模块都重要,但与核心业务逻辑无关。
在Java中,Spring框架广泛支持AOP,例如我们可以使用Spring
AOP来添加日志记录的切面,主要代码如下 1234567@Aspectpublic class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void logBefore(JoinPoint joinPoint) { S ...