XuQi's Blog

  • 首页

  • 归档

C++常用工具类

发表于 2019-07-01 更新于 2019-10-20

编码规范

https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/

禁止拷贝基类

为什么禁用拷贝(复制)构造函数
  • 浅拷贝问题

    默认生成的拷贝构造函数会直接拷贝成员指针的地址,这样两个对象的指针指向同一个缓冲区,导致析构的时候两次删除同一片区域的问题(这个问题又叫双杀问题)

    解决办法:

    1. 自己编写拷贝构造函数

    2. 使用 shared_ptr 这样的智能指针

    3. 禁用拷贝构造函数和赋值操作符

  • 基类拷贝问题

    如果我们不去自己编写拷贝构造函数,编译器默认生成的版本会自动调用基类的拷贝构造 函数完成基类的拷贝,但是如果我们出于某种原因编写了,自己编写了拷贝构造函数(比如因为上文中提到的浅 拷贝问题),编译器不会帮我们安插基类的拷贝构造函数,它只会在必要的时候帮我们安 插基类的默认构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
//禁止拷贝基类
class noncopyable
{
protected: // 为什么声明成为 protected
noncopyable() {}
~noncopyable() {}
private:
//禁止拷贝
noncopyable(const noncopyable &that) = delete;
noncopyable(noncopyable &&that) = delete;
noncopyable &operator=(const noncopyable &that) = delete;
noncopyable &operator=(noncopyable &&that) = delete;
};

StrPrinter 辅助类

字符串拼接输出

1
string output = StrPrinter << "a" << "b" << "c" << "d" << endl;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define StrPrinter _StrPrinter()

class _StrPrinter : public string {
public:
_StrPrinter() {}

template<typename T>
_StrPrinter& operator <<(T && data) { // 右值引用
_stream << std::forward<T>(data); // 按照原有的类型转换,不管是const还是非const
this->string::operator=(_stream.str()); //调用基类的赋值函数,把值放在内部
return *this;
}
string operator <<(std::ostream&(*f)(std::ostream&)) const {
return *this;
}
private:
stringstream _stream;
};

Logger 辅助类

LogContextCapturer
  • 传入Logger对象,用于实现log输出方式
  • 实现 操作符<<,用于输出log
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
/**
* 可以有多个Capturer,每个Capture可以logContext,他们使用同一个logger用于输出Log
*/
class LogContextCapturer : public noncopyable { //禁止拷贝构造函数
public:
LogContextCapturer(Logger &logger, LogLevel level, const char *file, const char *function, int line)
: m_pLogContext(new LogContext(level, file, function, line)), m_logger(logger) {}

LogContextCapturer &operator<<(ostream &(*f)(ostream &)) {
if (!m_pLogContext) {
return *this;
}
m_logger.write(m_pLogContext); // 将LogContext 的内容传给Logger,并通过Logger传给不同的channel或者writter
m_pLogContext.reset(); // 清空log的内容
return *this;
}

template<typename T>
LogContextCapturer &operator<<(T &&data) {

if (!m_pLogContext) {
return *this;
}
(*m_pLogContext) << std::forward<T>(data); // 将log存到自己的logContext中

return *this;
}

private:
LogContextPtr m_pLogContext; // 用于保存log的内容
Logger &m_logger; // Log对象的输出
};
LogContext
  • 继承于ostringstream
  • 保存log的基本信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Log的内容
*/
class LogContext : public ostringstream {
public:
friend class LogContextCapturer;

public:
LogLevel m_level;
int m_line;
const char *m_pFile;
const char *m_pFunction;
struct timeval _tv;
private:
LogContext(LogLevel level, const char *file, const char *function, int line)
: m_level(level),
m_line(line),
m_pFile(file),
m_pFunction(function) {
gettimeofday(&_tv, NULL);
}
};
Logger
  • 可以追加多个Channel
  • 统一调整每个Channel的等级
  • 可以设置writer,如果设置writer,则用writer输出各个Channel,如果没设置,则用默认channel输出
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
/**
* 每个Logger对象有一个Channel Map 和writer
* 如果writer为空,则写入channles,否则写入writer
*/
class Logger : public std::enable_shared_from_this<Logger>, public noncopyable {
public:

friend class LogContextCapturer;

void setWriter(const std::shared_ptr<LogWriter> &writer) {
m_pWriter = writer;
}

void setLevel(LogLevel level) {
for (auto &ch : m_ChannelsPoint) {
ch.second->setLevel(level);
}
}

private:

void writeChannels(const LogContextPtr &stream) {
for (auto &c : m_ChannelsPoint) {
c.second->write(*this, stream);
}
}

void write(const LogContextPtr &stream) {
if (m_pWriter) {
m_pWriter->write(stream);
} else {
writeChannels(stream);
}
}

map<string, std::shared_ptr<LogChannel> > m_ChannelsPoint;
std::shared_ptr<LogWriter> m_pWriter;
string m_loggerName;
};
LogChannel
  • Channel的基类,子类要重写write,需要调用format格式化输出log
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
class LogChannel : public noncopyable {
public:

virtual void write(const Logger &logger, const LogContextPtr &stream) = 0;

static std::string printTime(const timeval &tv) {
time_t sec_tmp = tv.tv_sec;
struct tm *tm = localtime(&sec_tmp);
char buf[128];
snprintf(buf, sizeof(buf), "%d-%02d-%02d %02d:%02d:%02d.%03d",
1900 + tm->tm_year,
1 + tm->tm_mon,
tm->tm_mday,
tm->tm_hour,
tm->tm_min,
tm->tm_sec,
(int) (tv.tv_usec / 1000));
return buf;
}

protected:
// 格式化字符串
virtual void format(const Logger &logger,
ostream &ost,
const LogContextPtr &logContext,
bool enableColor = true,
bool enableDetail = true);
protected:
string m_name;
LogLevel m_level; // 每个Channel都有自己的等级
};
ConsoleChannel
1
2
3
4
5
6
void ConsoleChannel::write(const Logger &logger, const LogContextPtr &logContext) {
if (m_level > logContext->m_level) {
return;
}
format(logger,std::cout,logContext, true,true);
}
FileChannel
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
void FileChannel::write(const Logger &logger, const LogContextPtr &logContext) {
if (m_level > logContext->m_level) {
return;
}
if (!m_fstream.is_open()) {
open();
}
format(logger, m_fstream, logContext, false);
}

void FileChannel::open() {
// Ensure a path was set
if (m_path.empty()) {
throw runtime_error("Log file path must be set.");
}
// Open the file stream
m_fstream.close();
m_fstream.open(m_path.c_str(), ios::out | ios::app);
// Throw on failure
if (!m_fstream.is_open()) {
throw runtime_error("Failed to open log file: " + m_path);
}
}
void FileChannel::close() {
m_fstream.close();
}
SysLogChannel

Linux C中提供一套系统日记写入接口,包括三个函数:openlog,syslog和closelog。调用openlog是可选择的。如果不调用openlog,则在第一次调用syslog时,自动调用openlog。调用closelog也是可选择的,它只是关闭被用于与syslog守护进程通信的描述符。

其中openlog和closelog都是可选的。不过,通过调用openlog,我们可以指定ident参数。这样,ident将被加到每条日记记录中。

1
2
3
4
5
6
7
8
#include <syslog.h>   
int main(int argc, char *argv[])
{
openlog("testsyslog", LOG_CONS | LOG_PID, 0);
syslog(LOG_USER | LOG_INFO, "syslog test message generated in program %s \n", argv[0]);
closelog();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void SysLogChannel::write(const Logger &logger, const LogContextPtr &logContext) {
if (m_level > logContext->m_level) {
return;
}
static int s_syslog_lev[10];
s_syslog_lev[LTrace] = LOG_DEBUG;
s_syslog_lev[LDebug] = LOG_INFO;
s_syslog_lev[LInfo] = LOG_NOTICE;
s_syslog_lev[LWarn] = LOG_WARNING;
s_syslog_lev[LError] = LOG_ERR;

syslog(s_syslog_lev[logContext->m_level],"-> %s %d\r\n",logContext->m_pFile,logContext->m_line);
syslog(s_syslog_lev[logContext->m_level], "## %s %s | %s %s\r\n", printTime(logContext->_tv).data(),
LOG_CONST_TABLE[logContext->m_level][2], logContext->m_pFunction, logContext->str().c_str());
}
AsyncLogWriter
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
AsyncLogWriter::AsyncLogWriter(Logger &logger) : m_exit_flag(false),m_logger(logger) {
m_thread = std::make_shared<thread>([this]() { this->run(); });
}

AsyncLogWriter::~AsyncLogWriter() {
m_exit_flag = true;
m_sem.post();
m_thread->join();
flushAll();
}

void AsyncLogWriter::write(const LogContextPtr &logContext) {
{
lock_guard<mutex> lock(m_mutex);
m_pending.emplace_back(logContext);
}
m_sem.post();
}

void AsyncLogWriter::run() {
while (!m_exit_flag) {
m_sem.wait();
flushAll();
}
}
void AsyncLogWriter::flushAll(){
list<LogContextPtr> tmp;
{
lock_guard<mutex> lock(m_mutex);
tmp.swap(m_pending);
}

for (const auto & ctx : tmp)
{
m_logger.writeChannels(ctx);
}
}

ResourcePool循环池辅助类

限制资源的总数,防止资源过多,每次析构时,会根据池中的数量,决定是放回池中,还是删除。

测试程序
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

class string_imp : public string {
public:
template<typename ...ArgTypes>
string_imp(ArgTypes &&...args) : string(std::forward<ArgTypes>(args)...) {
DebugL << "创建string对象:" << this << " " << *this;
};
~string_imp() {
WarnL << "销毁string对象:" << this << " " << *this;
}
};

int main() {
//初始化日志
Logger::Instance().add(std::make_shared<ConsoleChannel>());
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
{
//大小为50的循环池
ResourcePool<string_imp> pool;
pool.setSize(2);

//获取一个对象,该对象将被主线程持有,并且不会被后台线程获取并赋值
auto reservedObj = pool.obtain();

reservedObj.reset();

{
WarnL << "主线程打印:开始测试主动放弃循环使用功能";

std::list<decltype(pool)::ValuePtr> objlist;
for (int i = 0; i < 4; ++i) {
reservedObj = pool.obtain();
string str = StrPrinter << i << " " << (i % 2 == 0 ? "此对象将脱离循环池管理" : "此对象将回到循环池");
reservedObj->assign(str);
reservedObj.quit(i % 2 == 0);
objlist.emplace_back(reservedObj);
}

}
ErrorL << "循环结束没有放回池中的对象都被析构,进入池的对象不会析构⬆";
}
ErrorL << "pool退出,析构pool中的对象⬆";

return 0;
}
shared_ptr_imp
  • 析构时,自动放回池或者删除
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
template<typename C>
class shared_ptr_imp : public std::shared_ptr<C> {
public:
shared_ptr_imp() {}

/**
* 构造智能指针
* @param ptr 裸指针
* @param weakPool 管理本指针的循环池
* @param quit 对接是否放弃循环使用
*/
shared_ptr_imp(C *ptr,const std::weak_ptr<ResourcePool_l<C> > &weakPool , const std::shared_ptr<atomic_bool> &quit) ;

/**
* 放弃或恢复回到循环池继续使用
* @param flag
*/
void quit(bool flag = true){
if(_quit){
*_quit = flag;
}
}
private:
std::shared_ptr<atomic_bool> _quit;
};

template<typename C>
shared_ptr_imp<C>::shared_ptr_imp(C *ptr,
const std::weak_ptr<ResourcePool_l<C> > &weakPool ,
const std::shared_ptr<atomic_bool> &quit ) :
shared_ptr<C>(ptr,[weakPool,quit](C *ptr){
auto strongPool = weakPool.lock(); // 变成share_ptr
if (strongPool && !(*quit)) {
//循环池还在并且不放弃放入循环池 析构时自动释放
strongPool->recycle(ptr);
} else {
delete ptr;
}
}),_quit(quit) {}
}
ResourcePool
  • 设置pool大小
  • 获取元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template<typename C>
class ResourcePool {
public:
typedef shared_ptr_imp<C> ValuePtr;
ResourcePool() {
pool.reset(new ResourcePool_l<C>());
}
#if defined(SUPPORT_DYNAMIC_TEMPLATE)
template<typename ...ArgTypes>
ResourcePool(ArgTypes &&...args) {
pool = std::make_shared<ResourcePool_l<C> >(std::forward<ArgTypes>(args)...);
}
#endif //defined(SUPPORT_DYNAMIC_TEMPLATE)
void setSize(int size) { // 设置资源池的大小
pool->setSize(size);
}
ValuePtr obtain() { // 获取一个元素
return pool->obtain();
}
private:
std::shared_ptr<ResourcePool_l<C> > pool;
};
ResourcePool_l
  • 实现创建对象
  • 获取对象
  • 回收对象,池满删除
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
template<typename C>
class ResourcePool_l: public enable_shared_from_this<ResourcePool_l<C> > {
public:
typedef shared_ptr_imp<C> ValuePtr;
friend class shared_ptr_imp<C>;
ResourcePool_l() {
// 定义一个创建对象的指针
_allotter = []()->C* {
return new C();
};
}

#if defined(SUPPORT_DYNAMIC_TEMPLATE)
template<typename ...ArgTypes>
ResourcePool_l(ArgTypes &&...args) {
_allotter = [args...]()->C* {
return new C(args...);
};
}
#endif //defined(SUPPORT_DYNAMIC_TEMPLATE)

~ResourcePool_l(){
std::lock_guard<decltype(_mutex)> lck(_mutex);
_objs.for_each([](C *ptr){
delete ptr;
});
}
void setSize(int size) {
_poolsize = size;
}

// 如果资源池中没有元素,则创建一个,如果有,则取出一个
ValuePtr obtain() {
std::lock_guard<decltype(_mutex)> lck(_mutex);
C *ptr;
if (_objs.size() == 0) {
ptr = _allotter();
} else {
ptr = _objs.front();
_objs.pop_front();
}
return ValuePtr(ptr,this->shared_from_this(),std::make_shared<atomic_bool>(false));
}
private:
void recycle(C *obj) {
std::lock_guard<decltype(_mutex)> lck(_mutex);
if ((int)_objs.size() >= _poolsize) {
delete obj;
return;
}
_objs.emplace_back(obj);
}
private:
List<C*> _objs;
function<C*(void)> _allotter;
mutex _mutex;
int _poolsize = 8;
};

ThreadPool线程池辅助类

ThreadPool = ThreadGroup+TaskQueue
  • ThreadPool 设置线程个数,
  • 设置线程池的优先级
  • 设置线程池是自动运行和手动运行
  • 插入线程池的最开始
  • 调整单个线程的优先级
  • 支持同步执行
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
class ThreadPool: public TaskExecutor
{
public:
enum Priority
{
PRIORITY_LOWEST = 0,
PRIORITY_LOW,
PRIORITY_NORMAL,
PRIORITY_HIGH,
PRIORITY_HIGHEST
};

//num:线程池线程个数
ThreadPool(int num = 1,
Priority priority = PRIORITY_HIGHEST,
bool autoRun = true)
:
_thread_num(num), _priority(priority)
{
if (autoRun) {
start();
}
_logger = Logger::Instance().shared_from_this();
}
~ThreadPool()
{
shutdown();
wait();
}

//把任务打入线程池并异步执行
Task::Ptr async(TaskIn &&task, bool may_sync = true) override
{
if (may_sync && _thread_group.is_this_thread_in()) {
task();
return nullptr;
}
auto ret = std::make_shared<Task>(std::move(task));
_queue.push_task(ret);
return ret;
}
// 放到最前面
Task::Ptr async_first(TaskIn &&task, bool may_sync = true) override
{
if (may_sync && _thread_group.is_this_thread_in()) {
task();
return nullptr;
}

auto ret = std::make_shared<Task>(std::move(task));
_queue.push_task_first(ret);
return ret;
}

uint64_t size()
{
return _queue.size();
}

static bool setPriority(Priority priority = PRIORITY_NORMAL,
thread::native_handle_type threadId = 0)
{

static int Min = sched_get_priority_min(SCHED_OTHER);
if (Min == -1) {
return false;
}
static int Max = sched_get_priority_max(SCHED_OTHER);
if (Max == -1) {
return false;
}
static int Priorities[] = { Min, Min + (Max - Min) / 4, Min
+ (Max - Min) / 2, Min + (Max - Min) * 3/ 4, Max };

if (threadId == 0) {
threadId = pthread_self();
}
struct sched_param params;
params.sched_priority = Priorities[priority];
return pthread_setschedparam(threadId, SCHED_OTHER, &params) == 0;

}

void start()
{
if (_thread_num <= 0)
return;
auto total = _thread_num - _thread_group.size();
for (int i = 0; i < total; ++i) {
_thread_group.create_thread(bind(&ThreadPool::run, this));
}
}

private:
void run()
{
ThreadPool::setPriority(_priority);
Task::Ptr task;
while (true) {
startSleep();
if (!_queue.get_task(task)) {
//空任务,退出线程
break;
}
sleepWakeUp();
try {
(*task)();
task = nullptr;
}
catch (std::exception &ex) {
ErrorL << "ThreadPool执行任务捕获到异常:" << ex.what();
}
}
}

void wait()
{
_thread_group.join_all();
}

void shutdown()
{
_queue.push_exit(_thread_num);
}
private:
TaskQueue<Task::Ptr> _queue;
thread_group _thread_group;
int _thread_num;
Priority _priority;
Logger::Ptr _logger;
};
TaskExecutor
  • 纯虚函数async
ThreadGroup (创建就运行)
  • 可以判断当前线程是否属于线程池
  • 添加线程create_thread 返回线程指针
  • 移除线程remove_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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class ThreadGroup {
private:
ThreadGroup(ThreadGroup const &);
ThreadGroup &operator=(ThreadGroup const &);
public:
ThreadGroup() {
}
~ThreadGroup() {
_threads.clear();
}

bool is_this_thread_in() {
auto thread_id = this_thread::get_id();
if (_thread_id == thread_id) {
return true;
}
return _threads.find(thread_id) != _threads.end();
}

bool is_thread_in(thread *thrd) {
if (!thrd) {
return false;
}
auto it = _threads.find(thrd->get_id());
return it != _threads.end();
}

template<typename F>
thread *create_thread(F &&threadfunc) {
auto thread_new = std::make_shared<thread>(threadfunc);
_thread_id = thread_new->get_id();
_threads[_thread_id] = thread_new;
return thread_new.get();
}

void remove_thread(thread *thrd) {
auto it = _threads.find(thrd->get_id());
if (it != _threads.end()) {
_threads.erase(it);
}
}
void join_all() {
if (is_this_thread_in()) {
throw runtime_error("thread_group: trying joining itself");
}
for (auto &it : _threads) {
if (it.second->joinable()) {
it.second->join(); //等待线程主动退出
}
}
_threads.clear();
}
size_t size() {
return _threads.size();
}
private:
unordered_map<thread::id, std::shared_ptr<thread> > _threads;
thread::id _thread_id;
};
TaskQueue
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
template<typename T>
class TaskQueue {
public:
//打入任务至列队
template<typename C>
void push_task(C &&task_func) {
{
lock_guard<decltype(_mutex)> lock(_mutex);
_queue.emplace_back(std::forward<C>(task_func));
}
_sem.post();
}
template<typename C>
void push_task_first(C &&task_func) {
{
lock_guard<decltype(_mutex)> lock(_mutex);
_queue.emplace_front(std::forward<C>(task_func));
}
_sem.post();
}
//清空任务列队
void push_exit(unsigned int n) {
_sem.post(n);
}
//从列队获取一个任务,由执行线程执行
bool get_task(T &tsk) {
_sem.wait();
lock_guard<decltype(_mutex)> lock(_mutex);
if (_queue.size() == 0) {
return false;
}
//改成右值引用后性能提升了1倍多!
tsk = std::move(_queue.front());
_queue.pop_front();
return true;
}
uint64_t size() const {
lock_guard<decltype(_mutex)> lock(_mutex);
return _queue.size();
}
private:
//经过对比List,std::list,std::deque三种容器发现,
//在i5-6200U单线程环境下,执行1000万个任务时,分别耗时1.3,2.4,1.8秒左右
//所以此处我们替换成性能最好的List模板
list<T> _queue;
mutable mutex _mutex;
semaphore _sem;
};
# C++11
MySQL数据库
鱼骨图分析法
  • 文章目录
  • 站点概览

XuQi

44 日志
30 标签
  1. 1. 编码规范
  2. 2. 禁止拷贝基类
    1. 2.1. 为什么禁用拷贝(复制)构造函数
  3. 3. StrPrinter 辅助类
  4. 4. Logger 辅助类
    1. 4.1. LogContextCapturer
    2. 4.2. LogContext
    3. 4.3. Logger
    4. 4.4. LogChannel
      1. 4.4.1. ConsoleChannel
      2. 4.4.2. FileChannel
      3. 4.4.3. SysLogChannel
      4. 4.4.4. AsyncLogWriter
  5. 5. ResourcePool循环池辅助类
    1. 5.1. 测试程序
    2. 5.2. shared_ptr_imp
    3. 5.3. ResourcePool
    4. 5.4. ResourcePool_l
  6. 6. ThreadPool线程池辅助类
    1. 6.1. ThreadPool = ThreadGroup+TaskQueue
      1. 6.1.1. TaskExecutor
    2. 6.2. ThreadGroup (创建就运行)
    3. 6.3. TaskQueue
© 2019 XuQi
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Muse v7.3.0