我的博文

我的开源项目

C:
libwebsockets(官方repo稍作改动)
zBuffer
C++:
个人通用开发工具库
基于libasound库音频播放
基于rostopic的服务调用封装
Qt:
Qt异步事件库

原文链接:
https://blog.csdn.net/oyoung_2012/article/details/102696213

运行环境:Ubuntu:16.04

开发环境准备

安装libsdl2-mixer-dev

1
~$ sudo apt install libsdl2-mixer-dev

需要包含的头文件

1
2
#include <SDL2/SDL.h>
#include <SDL2/SDL_mixer.h>

需要链接的库文件

1
2
3
4
5
set(CMAKE_CXX_FLAGS "-pthread ${CMAKE_CXX_FLAGS}")
target_link_libraries(app
SDL2
SDL2_mixer
)

流程

-> Mix_Init
-> Mix_OpenAudio
-> Mix_LoadMUS / Mix_LoadWAV
-> Mix_PlayMusic / Mix_FadeInMusic / Mix_PlayChanel / Mix_FadeInChannel
-> Others User Operations
-> Mix_HaltMusic / Mix_FadeOutMusic / Mix_HaltChannel / Mix_FaleOutChannel
-> Mix_CloseAudio
-> Mix_Quit

钩子函数

  • Mix_HookMusic: 用于设置音乐播放过程中的回调
  • Mix_HookMusicFinished: 用于设置音乐播放结束时的回调

示例代码(C++11)

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <SDL2/SDL.h>
#include <SDL2/SDL_mixer.h>
#include <oyoung/defer.hpp>
#include <sspdlog/sspdlog.h>

#include <csignal>
#include <functional>

static std::function<void()> on_signal;

void signal_TERM_handle(int) {
if(on_signal) on_signal();
}

int main(int,char**)
{
USE_DEFER;

on_signal = [&] {
__defer__.~Defer();
};

std::signal(SIGTERM, signal_TERM_handle);

auto n = Mix_Init(MIX_INIT_OGG | MIX_INIT_MP3 | MIX_INIT_FLAC);

if(n == 0) {
SSPD_LOG_ERROR << "[Mix_Init] Failed initialization: " << Mix_GetError();
return n;
}

SSPD_LOG_INFO << "[Mix_Init] Mix initialize successfully";

defer [=] {
SSPD_LOG_INFO << "[Mix_Quit] Mix will quit";
Mix_Quit();
};

n = Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 1024);

if(n < 0) {
SSPD_LOG_ERROR << "[Mix_OpenAudio] Failed open audio: " << Mix_GetError();
return n;
}

SSPD_LOG_INFO << "[Mix_OpenAudio] Open Audio successfully";

defer [=] {
SSPD_LOG_INFO << "[Mix_CloseAudio] Mix will close audio";
Mix_CloseAudio();
};

auto music = Mix_LoadMUS("/home/oyoung/Music/8.mp3");

if(music == nullptr) {
SSPD_LOG_ERROR << "[Mix_LoadMUS] Failed load music: " << Mix_GetError();
return -1;
}

SSPD_LOG_INFO << "[Mix_LoadMUS] Load music successfully";

defer [=] {
SSPD_LOG_INFO << "[Mix_FreeMusic] Music will be free";
Mix_FreeMusic(music);
};

n = Mix_FadeInMusic(music, 0, 1000);

Mix_HookMusicFinished([] {
SSPD_LOG_INFO << "[Main] Music play finished";
});


if(n < 0) {
SSPD_LOG_ERROR << "[Mix_FadeInMusic] Failed fade in music: " << Mix_GetError();
return n;
}

SSPD_LOG_INFO << "[Mix_FadeInMusic] Music start playing";

defer [=] {
SSPD_LOG_INFO << "[Mix_FadeOutMusic] Mix will fade out music";
Mix_FadeOutMusic(1000);
};

while (Mix_PlayingMusic()) {
SDL_Delay(1000);
}



return 0;
}

控制台输出:

1
2
3
4
5
6
7
8
9
[2019-10-23 10:30:55.443]-[info]- [Mix_Init] Mix initialize successfully (main.cc #32 main)
[2019-10-23 10:30:55.458]-[info]- [Mix_OpenAudio] Open Audio successfully (main.cc #46 main)
[2019-10-23 10:30:55.458]-[info]- [Mix_LoadMUS] Load music successfully (main.cc #60 main)
[2019-10-23 10:30:55.458]-[info]- [Mix_FadeInMusic] Music start playing (main.cc #79 main)
[2019-10-23 10:31:00.712]-[info]- [Mix_FadeOutMusic] Mix will fade out music (main.cc #82 operator())
[2019-10-23 10:31:00.713]-[info]- [Mix_FreeMusic] Music will be free (main.cc #63 operator())
[2019-10-23 10:31:01.724]-[info]- [Main] Music play finished (main.cc #70 operator())
[2019-10-23 10:31:01.739]-[info]- [Mix_CloseAudio] Mix will close audio (main.cc #49 operator())
[2019-10-23 10:31:01.755]-[info]- [Mix_Quit] Mix will quit (main.cc #35 operator())

原文链接:
https://blog.csdn.net/oyoung_2012/article/details/102619309

需求说明

当需要拷贝多个文件并且需要保留源文件的目录树结构时, 如果源目录的文件比较纯净,没有其他相关的文件或目录时, 我们只需要执行

1
2
~$ #cp -r <源目录> <新目录>
~$ cp -r workspace/project workspace/Cpp

但是, 如果我们需要拷贝的文件所在的目录里包含了其他项目或程序的文件或目录, 那我们就不能这么干

比如,

  • 在 /etc或者/usr/local/bin下面有很多我们自己项目的相关配置文件和程序文件, 并且我们知道这些文件路径
  • 包含指定字符串的文件名, 比如 *.log
  • 我们自己制作或者我们比较感兴趣的其他厂商的 *.deb程序包(Ubuntu使用软件包)

针对自己的项目配置文件

我们可以新建一个文本文档, 里面把我们所关心的文件列出来

list.txt

1
2
3
4
5
/etc/app/app.cnf
/etc/app/conf.d/a.cnf
/etc/app/conf.d/b.cnf
/etc/app/conf.d/c.cnf
/etc/app/conf.d/d.cnf

这样我们就可以通过使用 cat list.txt 获取到我们的文件路径列表

此时我们只需要使用

1
~$ cp --parent $(cat list.txt) .

就可以将列表里面的所有文件按照原有的目录树结构拷贝到当前目录(或者使用其他目录)

针对包含指定字符串的文件名我们可以用 find 命令获取到文件列表

1
2
3
4
5
6
7
~$ find /opt/ros/ -name *.a
/opt/ros/kinetic/lib/liboctomath.a
/opt/ros/kinetic/lib/libcsm-static.a
/opt/ros/kinetic/lib/liboptions.a
/opt/ros/kinetic/lib/libegsl.a
/opt/ros/kinetic/lib/liboctomap.a
~$

然后, 嘿嘿

1
~$ cp --parent $(find /opt/ros -name *.a) .

这不就都按照源目录结构拷贝到当前目录了嘛

针对.deb包的文件列表, 我们也有命令可以获取到路径列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
~$ dpkg -L mysql-server-5.7 
/etc
/etc/mysql
/etc/mysql/mysql.conf.d
/etc/mysql/mysql.conf.d/mysqld_safe_syslog.cnf
/etc/mysql/mysql.conf.d/mysqld.cnf
/etc/mysql/debian-start
/etc/mysql/mysql.cnf
/etc/apparmor.d
/etc/apparmor.d/usr.sbin.mysqld
/etc/logrotate.d
/etc/logrotate.d/mysql-server
/etc/init.d
/etc/init.d/mysql
/etc/logcheck
/etc/logcheck/ignore.d.server
/etc/logcheck/ignore.d.server/mysql-server-5_7
/etc/logcheck/ignore.d.workstation
/etc/logcheck/ignore.d.workstation/mysql-server-5_7
/etc/logcheck/ignore.d.paranoid
/etc/logcheck/ignore.d.paranoid/mysql-server-5_7
/etc/init
/etc/init/mysql.conf
~$

这个时候我们就可以…. -_- 嘿嘿嘿 , 你懂的

原文链接:
https://blog.csdn.net/oyoung_2012/article/details/101072215

MySQL 客户端 api (libmysqlclient-dev)的简单使用

几个常用重要接口

  • mysql_library_init()
  • mysql_library_end()
  • mysql_init()
  • mysql_connect() / mysql_real_connect()
  • mysql_query() / mysql_real_query()
  • mysql_num_fileds()
  • mysql_num_rows()
  • mysql_fetch_field() / mysql_fetch_fields()
  • mysql_fetch_row()
  • mysql_affected_row()

几个重要数据类型

  • MYSQL
  • MYSQL_RES
  • MYSQL_ROW
  • MYSQL_FIELD

编译选项

cmake

1
set(CMAKE_CXX_FLAGS "-g -lmysqlclient -pthread -lz -lm -lrt -ldl $CMAKE_CXX_FLAGS")

g++ build

1
g++ -o mysql -lmysqlclient -I/usr/include/mysql -L/usr/lib/x86_64-linux-gnu -pthread -lz -lm -lrt -ldl -g main.cc

示例程序

小提示:

示例代码中使用到了另外两个三方库
一个三方库时sspdlog, 日志库
#include <sspdlog/sspdlog.h> 可以替换成 <iostream>,
SSPD_LOG_INFO可以替换为std::cout,
SSPD_LOG_ERROR可以替换为std::cerr
另一个三方库 为 自己的 工具库 oyoungs/dispatch, 可以到github上下载

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225

#include <mysql/mysql.h>
#include <sspdlog/sspdlog.h>
#include <oyoung/format.hpp>

namespace mysql {
struct library {
library(int argc, char **argv, char **groups) {
if(mysql_library_init(argc, argv, groups)) {
throw std::runtime_error("MYSQL Initialize failed");
}
}

~library() {
mysql_library_end();
}
};

struct row {

row(MYSQL_ROW row): m_row(row) {}


~row() {}

operator bool() const {
return m_row;
}

std::string operator[](std::size_t index) const {
return m_row[index];
}

row(const row&) = delete;
row(row&& other): m_row(other.m_row) {
other.m_row = nullptr;
}

private:
MYSQL_ROW m_row{nullptr};
};

struct field {

field(MYSQL_FIELD *field): m_field(field) {}

std::string name() const {

return m_field->name;
}

std::string catalog() const {
return std::string(m_field->catalog, m_field->catalog_length);
}



~field() {

}

field(const field&) = delete;
field(field&& other): m_field(other.m_field) {
other.m_field = nullptr;
}

protected:
MYSQL_FIELD *m_field{nullptr};
};

struct fields : field {

fields(MYSQL_FIELD *f) : field(f) {}

field operator[](std::size_t index) {
return m_field + index;
}
};

struct result {
result() = default;
result(MYSQL_RES *res): m_result(res) {}



~result() {
if(m_result) {
::mysql_free_result(m_result);
}
}


result(const result&) = delete;
result(result&& other): m_result(other.m_result) {
other.m_result = nullptr;
}

row fetch_row() {
return mysql_fetch_row(m_result);
}

std::uint64_t rows() const {
return mysql_num_rows(m_result);
}

unsigned field_count() const {
return ::mysql_num_fields(m_result);
}

fields fetch_fields() {
return mysql_fetch_fields(m_result);
}




private:
MYSQL_RES *m_result {nullptr};
};



struct client {
client()
: m_client(mysql_init(nullptr)) {

}

bool good() const {
return nullptr != m_client;
}

bool not_good() const {
return nullptr == m_client;
}

bool connect(const std::string& host, const std::string& user, const std::string& password, const std::string& database,
unsigned short port = 3306) {
return mysql_real_connect(m_client, host.c_str(), user.c_str(), password.c_str(), database.c_str(), port, nullptr, 0) != nullptr;
}

bool query(const std::string& sql) {
return mysql_real_query(m_client, sql.c_str(), sql.length()) == 0;
}

std::uint64_t affected_rows() const {
return mysql_affected_rows(m_client);
}

std::uint64_t inserted_id() const {
return mysql_insert_id(m_client);
}

result use_result() const {
return result(mysql_use_result(m_client));
}



~client() {

}

std::string error() const {
return mysql_error(m_client);
}

private:
MYSQL *m_client;
};
}

int main(int argc, char**argv) try {

mysql::library library(argc, argv, nullptr);
mysql::client client{};

if(client.not_good()) {
SSPD_LOG_ERROR << "Create MySQL client failed";
return -1;
}

if(!client.connect("172.17.0.2", "blog", "blog.123", "blog")) {
SSPD_LOG_ERROR << "MySQL connect failed: " << client.error();
return -2;
}


if(!client.query("INSERT INTO user set name='blog', email='blog@hotmail.com'")) {
SSPD_LOG_ERROR << "MySQL query INSERT failed: " << client.error();
return -3;
}

SSPD_LOG_INFO << "MySQL affected rows after insert: " << client.affected_rows();
SSPD_LOG_INFO << "MySQL last inserted ID: " << client.inserted_id();


if(!client.query("SELECT id, name, email FROM user")) {
SSPD_LOG_ERROR << "MySQL query SELECT failed: " << client.error();
return -3;
}

auto result = client.use_result();

auto field_count = result.field_count();
auto fields = result.fetch_fields();

while (auto row = result.fetch_row()) {
for(auto i = 0; i < field_count; ++i) {
SSPD_LOG_INFO << fields[i].name() << ": " << row[i];
}
}

if(!client.query(oyoung::format("DELETE FROM user WHERE id=%1").arg(client.inserted_id()).to_string())) {
SSPD_LOG_ERROR << "MySQL query DELETE failed: " << client.error();
return -3;
}

SSPD_LOG_INFO << "MySQL affected rows after delete: " << client.affected_rows();


return 0;
} catch(const std::exception& e) {
SSPD_LOG_ERROR << e.what();
}

问题提出

使用过swift开发过iOS应用的广大搬砖工都知道, 在swift语言里, 有一个很好用的关键字 defer
这个defer有什么用呢?
在某个函数或者代码块中, 如果使用了defer 添加了一段代码, 会发生什么呢?
哈哈, 当然是 在代码块结束时, 一定会调用这段代码了。
这样有什么好处呢?
我们知道, 有些操作需要很多步骤,其中一步出错,就可能需要是整个操作流程结束, 但是结束之前那些已经申请的资源怎么办呢? 如果不处理肯定会出现资源泄露的啊。这是defer就派上用场了,使用defer 之后, 在后面的代码块中去释放申请占用的资源, 就会保证在代码块结束之前一定会调用这个defer代码块来释放资源, 保证了一定的安全性

问题思路

那作为一名C++开发者来说,有没有这个便利的工具呢?
老实说, 从原生C++角度来说, 是没有这个工具的,有点可惜! 那我们能不能自己实现呢?

思路分析

1.  defer 功能是退出代码块时必定会执行代码, C++里面有这种机制吗? 当然有啊, 局部对象的析构函数, 可行性 +1
2.  defer 后面接的是代码块, C++11以后, 也有了lambda表达式的支持, 可行性再次 +1
3.  defer 后面所接的代码块是不带括号尾部闭包格式, C++ 有什么方式可以以函数对象为参数且不需要括号就能调用的呢? 哈哈, 有, + - * /  << >> +=  -= 等等操作符调用时不需要加括号, 可行性再次 +1

如何实现

  • 需要定义一个类
  • 这个类可以通过调用操作符的方式往内部追加一个代码块
  • 该类的对象析构时, 一定会去调用内部保存的代码块

动手试试

不如我们就定义一个类叫Defer好了, 至于用哪个操作符呢? 凭自己喜好啊, 我这里使用了 += 表示往里头追加, 哈哈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <vector>
#include <functional>
struct Defer {
Defer& operator+=(std::function<void()> block) {
m_defers.emplace_back(std::move(block));
return *this;
}

~Defer() {
while (!m_defers.empty()) {
if(m_defers.back()) m_defers.back()();
m_defers.pop_back();
}
}
private:
std::vector<std::function<void()>> m_defers;
};

#define USE_DEFER Defer __defer__
#define defer __defer__ +=

测试看看?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

#include <iostream>
int main(int argc, char **argv)
{
USE_DEFER;

auto a = new int(10);
std::cout << "new a" << std::endl;

defer [=] {
std::cout << "delete a" << std::endl;
delete a;
};

auto b = new char[100];
std::cout << "new [] b" << std::endl;

defer [=] {
std::cout << "delete [] b" << std::endl;
delete [] b;
};

return 0;
}

输出结果是不是预期的呢?

1
2
3
4
new a
new [] b
delete [] b
delete a

哈哈, 还真是!! 功能实现了

更多

这里的 defer 还能用在类中, 确保资源在类对象析构的时候能正确的释放资源

比如

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

class File {
public:
File(std::string path)
: m_path(std::move(path)) {
defer [=] {
if(m_descriptor > 0) {
std::cout << "close file " << m_path << std::endl;
::close(m_descriptor);
m_descriptor = -1;
}
};
}

void open() {
std::cout << "open file " << m_path << std::endl;
m_descriptor = ::open(m_path.c_str(), O_RDONLY);
}

private:
USE_DEFER;
std::string m_path;
int m_descriptor{-1};
};

int main(int, char**)
{

File file("./test.log");

file.open();


return 0;
}

预期结果
① 当文件不存在时:

1
open file ./test.log

② 当文件存在时:

1
2
open file ./test.log
close file ./test.log