标签归档:c

RSS feed of c

最后更新于 .

前段时间一直没写博客,昨天更新了一篇,今天突然又来了兴致,那就再更新一篇吧(所以说啊,治疗拖延症最好的方法就是现在开始做)

这篇还是一些技术的整理,主要是用于备忘,大家如果觉得太简单就一笑而过啦~

一. python通过图片内容判断图片类型

前段时间写了一个小站练手,http://xiangshuguo.com,一个支持自由上传的图片小站。

因为要限制上传图片的格式,所以要做文件类型检测,代码如下:


def get_image_type(pd, is_path=True):
'''
获取图片的类型,支持传入路径和文件内容
'''
if is_path:
f = file(pd, 'rb')
data = f.read(10).encode('hex')
else:
data = pd.encode('hex')

ftype = None

if data.startswith('ffd8'):
ftype = 'jpeg'
if data.startswith('424d ...

最后更新于 .


C++的模板其实是个挺纠结的东西,用的不好的话,编译的一堆错误够你调到崩溃,但要是用的好呢,又确实非常方便,我们来看看

一.获取数组长度
比如


int arr[10];

怎么获取 arr 的长度呢?
最简单的代码:

uint32_t count = sizeof(arr) / sizeof(arr[0]);

但是这样也带来一个问题,万一是个新手程序员:

int *p = arr;
uint32_t count = sizeof(p) / sizeof(p[0]);

就有问题了……
那么有没有办法,有一种安全的方法,当发现传入的是指针的时候,自动编译报错呢?
有的,模板里面可以推导出数组的长度。
所以我们可以使用如下代码

template
size_t arrarysize(T (&array)[N]) { return N; }

这样,当传入指针的时候,编译就会报错了。
而同时你肯定也能看出,我们甚至能指定只能传入数组长度为固定某数字的数组了,怎么做我就不用讲了吧 ...

最后更新于 .


用C++越久,越是觉得C++太多陷阱,真是防不胜防。
我们看这样一段代码:


#include
using namespace std;
class C
{
public:
C(int a) {
printf("%d\n", __LINE__);
}
virtual ~C() {}

};
int main(int argc, char **argv)
{
C x1(1);
return 0;
}

编译执行正常,结果是:

7

然后我们改一下,把构造函数变成无参数的:


#include
using namespace std;
class C
{
public:
C() {
printf("%d\n", __LINE__);
}
virtual ~C() {}

};
int main(int ...

最后更新于 .

相信对于这个标题,用过lisp的朋友一定不陌生,本来也是准备了一大堆理论要讲,想了想还是直接举例子比较好。

就举最近产品提的一个产品需求吧,简单描述一下:


  1. 对于不同的第三方应用,有不同的频率限制。没有配置则使用默认值

  2. 对于不同的第三方应用,在不同的时间段,有不同的频率限制。没有配置则使用默认值

公司内部都是用C++,当时第一点想到的肯定是配置一个xml文件,里面配置上这些参数,在进程启动的时候,用tinnyxml或者其他xml解析器把xml解析成C++可以辨识的数据结构。
我们来看一下这个xml配置有多复杂:



























这个配置可以说已经非常复杂了,而最重要的是,这种产品上的需求,说变就变,如果真的现有的配置格式满足不了新需求,那么只能变更了。做哪些变更呢?


  1. 配置文件格式

  2. 配置文件解析代码

  3. 数据结构代码

  4. 计算逻辑

  5. 重新编译发布

一个小小的产品需求变更居然会带来这么多修改量,这是很不合理的,况且需求变更从来都是最频繁的事情。

我们对这五个操作步骤考虑一下:
如果把计算逻辑从C++中剥离出来,放在脚本里,由脚本解析器作为一个通用的配置文件解析工具,那么1,2,3,4,5步就都不需要修改任何C++框架代码

好吧,我们已经得到答案了,其实C++框架只是想要一个频率限制的值而已,那么这个值究竟是怎么算出来的,就交给脚本去做吧。
而对脚本来说,xml什么的配置文件完全没有必要,因为在python ...

最后更新于 .

C/C++代码中,野指针问题历来已久,当然,大家都知道new/delete要成对出现:


A *p = new A();
delete p;
p = NULL;

然而现实中却并不是总是如此简单,考虑如下例子:


class A
{
public:
C() {}
virtual ~C() {}
};
class B
{
public:
B() {
m_pA = NULL;
}
virtual ~B() {}

void SetA(A* p)
{
m_pA = p;
}

private:
A* m_pA;
};

A* pA = new A();
B* pB = new B();
pB->SetA(pA);

delete pA;
pA = NULL ...

最后更新于 .

上一篇文章《更简洁的C++数据库访问框架-soci》介绍了soci,但也同时提到了一个缺点---insert/update/delete操作的时候,没有返回影响了多少行。
另外还有一个隐性的原因,soci的代码大量使用了异常,而项目中要求底层代码是严禁抛出异常的(其实google也是有这个要求的)。
基于以上两个原因,所以最终没有把soci用在生产环境。

但是又不想每次都重复去调用那些繁琐的MySQL API,所以这次又重新对MySQL API做了一层C++的封装。

其实相信每个和mysql打过交道的程序员都应该会尝试去封装一套mysql的接口,这一次的封装已经记不清是我第几次了,但是每一次我希望都能做的比上次更好,更容易使用。

先来说一下这次的封装,遵守了几个原则,其中部分思想是从python借鉴过来的:


  • 1.简单

  • 简单,意味着不为了微小的效率提升,而去把接口搞的复杂。因为本身数据库存储效率的瓶颈并不是那一两次内存copy,代码中随处可以看到以这个为依据的设计。
  • 2.低学习成本

  • 使用一套新库通常意味着投入学习成本,而这次的封装并没有像django那样实现一套完整的模型系统,也没有做soci那样的语法分析器,我选择最简单易懂的方式:做sql语句拼接器,所以对习惯了使用原生mysql api的朋友,学习成本很低
  • 3.模块化

  • 代码实际包括了两个模块,一个是mysql client端的封装,一个是sql的拼接器,这两个模块是完全独立的,调用者可以任意组合或者独立使用。
  • 4.尽量使用STL以及模板,简化代码编写

  • 最大的特点就是大量使用了stringstream进行类型转化 ...

最后更新于 .


一.string中find_first_of的误用
STL中提供的string可以说极大方便了对字符串的操作,但是很多函数由于样子上很相似,所以导致很容易理解错误,find_first_of和find就是一个很好的例子。
我们先来看一下string提供的查找相关的函数列表:


find_first_of() 查找第一个与value中的某值相等的字符
find_first_not_of() 查找第一个与value中的所有值都不相等的字符
find_last_of() 查找最后一个与value中的某值相等的字符
find_last_not_of() 查找最后一个与value中的所有值都不相等的字符
rfind() 查找最后一个与value相等的字符(逆向查找)

如此简洁的说明,其实完全没有把他们最重要的区别描述出来,请务必记住:
对于find和rfind:


  • 匹配的是整个被查找串


对于find_first_of,find_first_not_of,find_last_of,find_last_not_of:

  • 匹配的是被查找串中的任意字符


我们来测试一下:

#include
#include
#include
#include
#include
#include
using namespace std;
int main(int argc, const char *argv[])
{
string src = "vimer.cn";

string str1 = "mer";
string str2 = "sre ...

最后更新于 .

接着上一篇文章: 有限状态机的C++实现(1)-epoll状态机,我们今天来介绍更复杂和深入的部分。 为什么会在标题中提到bayonet这个开源项目呢?笔者本人一直想要写一套架构优美、功能完善的异步server框架,也看过很多朋友、同事实现的版本,虽然功能上基本能满足需求,但是架构上我却始终觉得是有瑕疵的,直到后来和同事讨论,发现可以让一个客户端请求的到来作为一个session,而之后的每一次与其他server的交互都可以看作是一次状态转化,才感觉架构比较合理了。 简单来说即,一个session从开始到介绍会经历两种状态机的变化:
  • 1.业务逻辑层面的状态变化,例如先验证登录态,再验证权限,再获取用户资料
  • 2.每一个与其他server交互的socket自身的状态变化,如recv、send、等,而socket的状态变化会触发逻辑层的状态变化。
按照这种思路,目前的代码开发已经完成了70%,即可以正常的进行一个session的开始和结束,主要还缺一些细节的代码,比如超时的检测及超时之后的处理,健全的统计之类。好了,我们来用vs看一下代码的整体类图(图压缩比较严重,请单击后查看):

1

每个类的用处已经在途中简单说明了,这里就不再赘述,我们重点来看一下用这个框架来实现一个逻辑server时需要做哪些事情。 svr2目录下的main.cpp即实现了一个最简单的server,我们按部分来看其实现: 1.逻辑层状态的定义
class CAppFsmLogic1 : public CAppFsmBase ...

最后更新于 .

之前公司的同事写了一个基于epoll的网络服务器,其中涉及到socket状态的转化(如等待接收,接收中,接收完成等),以及socket之间的转化(如验证完ip权限之后,验证完登录态),可见是一个多层次的状态机。
但是在原来的实现中却并没有使用状态模式,导致整个逻辑非常复杂,状态之间的跳转也很难把握。本系列的文章将会通过状态模式来重构整套代码。

状态机模式本身这里就不做详细介绍了,读者可以google一下,笔者在仔细对比过《设计模式之禅》,《研磨设计模式》以及游戏中NPC状态机的实现之后,抽象了如下的一套接口.


/*=============================================================================
# Author: dantezhu - http://www.vimer.cn
# Email: zny2008@gmail.com
# FileName: interfaces.h
# Description: 公共接口
# Version: 1.0
# LastChange: 2011-01-19 23:24:33
# History:
=============================================================================*/
#ifndef _INTERFACES_H_
#define _INTERFACES_H_
#include
#include
using namespace std;

class IFsm ...

最后更新于 .

今天在工作上遇到一个问题,觉得很有代表性,特抽象如下:
通过设计模式的角度来说,就是模板方法,已经有一个基类,需要定义很多子类来实现其方法。
但是类名都只有一部分不同,且构造函数的入参也只有一部分不同。
如代码:


#include
#include
#include
#include
#include
using namespace std;
class EMA
{
public:
EMA(string a,string b,string c,string d,string e)
{
cout < }
};
class EMA4CGI_1ST:public EMA{
public:
EMA4CGI_1ST():EMA(
"app_mng.1ST_EMA_AVG",
"app_mng.1ST_EMA_HWM",
"app_mng.1ST_EMA_TMO",
"app_mng.1ST_EMA_N",
"app_mng.1ST_EMA_RATIO"
){}
~EMA4CGI_1ST() {}
};
class EMA4CGI_2ND:public EMA ...