存档

‘C/C++’ 分类的存档

Boost LRU-Cache使用方法简介

2017年11月13日 没有评论

    缓存是提高系统运行效率的常用组件,可以将“有效的”业务数据直接返回用户,避免繁琐的计算过程。除了Redis、MemCache等常用缓存系统,应用程序内部也可以根据需要设置一定容量的缓存,减少跨进程调用,提高效率。

    LRU是常用的缓存策略,可以将访问最 频繁的数据保存在有限的缓存中,提高缓存命中率。

    在C++中,可以通过map来保存数据键值对,并通过list将最近使用的数据保存在一端,从list的另一端来清除过期数据的方法实现一个缓存系统。

    然而,我们没必要自己再造一个轮子。Boost-1.65.1版本开始引入了lru_cache算法,通过模板的方式可以方便的实例化出各种类型的缓存对象。

    下面简单介绍boost lru_cache的使用方法。

#lru_cache头文件,这是个header-only库,不需要其他模块
#include <string>
#include <iostream>
using namespace std;
#include <boost/compute/detail/lru_cache.hpp>

int main()
{
    const int iCacheSize=100;
    #初始化时设置缓存容量
    boost::compute::detail::lru_cache<string, string> ssCache(iCacheSize);

    #插入数据
    ssCache.insert("ZhangSan", "Beijing");
    ssCache.insert("LiSi", "Shanghai");

    #查找并获取数据
    string s("ZhangSan");
    if(ssCache.contains(s))
    {
        #注意!get方法返回的是一个boost::optional<string>对象,而不是直接返回存入其中的Value类型的对象!
        boost::optional<string> o_Region=ssCache.get(s);
        #我们可以通过boost::optional<string>的get()来获取Value对象
        string sRegion=o_Region.get();
        cout << s << " comes from " << sRegion << endl;
    }

    return 0;
}
分类: C/C++, 开发 标签: , ,

c++点滴:string非空判断和随机访问的实现细节和使用方法

2017年10月17日 没有评论
  1. 非空判断
    • 一般通过是否等于”” 或者empty()函数的返回值来判断是否为空,结果是一致的
    • 差别在于:empty()返回的是string内置变量_M_rep()->_M_length的值,不需要其他计算;而与””比较是需要调用compare(const _CharT* __s)函数来判断的,因此在性能方面,empty()更占优
  2. 随机访问
    • 可通过[]或at()来随机访问,都是通过下标直接访问内部buffer,很快。_M_data()[__pos]
    • 差别在于:at()更安全。当下标超出string.size()时,at()抛出异常,而[]则引发断言错误。
分类: C/C++, 开发 标签:

分享一段解析HTTP请求消息头的C++代码

2017年10月16日 没有评论

Http请求消息说白了就是\r\n和:分割的一串字符,解析消息头部考察的是基本的字符串操作。当然实际应用中可以使用boost::splitl来辅助处理。

请求消息如下:

GET /suggest?word=http://t.&callback=suggest360&encodein=utf-8&encodeout=utf-8&_h=18.js&outfmt=json HTTP/1.1
Host: sug.so.360.cn:8188
Cache-Control: no-cache
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36 QIHU 360SE
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Accept: */*
Cookie: vjuids=a8f919aab.156c5a83b9b.0.3db0cfd7; shenhui12=w:1; gn12=w:1; sci12=w:1; UM_distinctid=15c7168d4207f-083dc20ce-4349052c-100200-15c7168d42346; sohutag=8HsmeSc5NSwmcyc5NSwmYjc5MSwmYSc5NywmZjc5NCwmZyc5NCwmbjc5NjwmaSc5NSwmdyc5NCwmaCc5NCwmYyc5NCwmZSc5NCwmbSc5NCwmdCc5NH0; _muid_=1501816572977065; _ga=GA1.2.1163329499.1503043055; beans_mz_userid=62mbb0GPSAy5; debug_test=sohu_third_cookie; lastpopup=2017-8-19; vjlast=1472194952.1505800087.21; IPLOC=CN1100; SUV=1608221725397504
Range: bytes=17276560-19912545
Referer: http://offp.download.icartoons.cn:8188/client_package/v_4125/7825

解析代码如下:

//存储请求行
string cmdLine="";  
//存储消息头各字段和值
map<string, string> kvs;  

size_t pos=0, posk=0, posv=0;
string k="", v="";
char c;
//http保存了上述GET请求
while(pos != http.size())  
{
    c=http.at(pos);
    if(c == ':')
    {
        //非请求行,且消息头名称未解析
        if(!cmdLine.empty() && k.empty())  
        {
            //存储消息头名称
            k = http.substr(posk, pos-posk);  
            //跳过冒号和空格
            posv = pos+2;  
        }
    }
    //行尾
    else if(c == '\r' || c=='\n')  
    {
        //尚未解析到消息头字段名称,且请求行也未解析过
        if(k.empty() && cmdLine.empty())  
        {
            //本行应是请求行,保存之
            cmdLine = http.substr(posk, pos-posk);  
        }
        else
        {
            //已解析了消息头字段名称,尚未解析字段值
            if(!k.empty() && v.empty())  
            {
                //存储字段值
                v = http.substr(posv, pos-posv);  
            }
        }
        posk=pos+1;
    }

    if(!k.empty() && !v.empty() && !cmdLine.empty())
    {
        //保存消息头字段名称和值
        kvs.insert(make_pair(k,v));  
        k="";
        v="";
    }
    ++pos;
}

需要注意的是:消息头各字段的值中也有可能出现冒号。

分类: C/C++, 开发 标签: ,

扑克洗牌算法(c++版)

2017年3月20日 没有评论

这是三年前去一家游戏公司面试时被问到的一个问题,当时由于自己在传统软件公司做业务,对算法没有留心学习,虽勉强答出来了,但从未实现过。今天随手写了个小程序做下验证。

基本思路:将代表扑克牌的52个数字放入vector中,每次随机抽取一张放入新的vector,并将被抽取牌从原vector中删除。如此循环52次,即可得到随机化的牌面。

代码如下:

#include <iostream>
using namespace std;
#include <vector>
#include <stdlib.h>

int main()
{
  unsigned int total;
  cout << "Please input the TOTAL:" << endl;  //输入52
  cin >> total;

  //将52个数放入vCards0

  vector<unsigned int> vCards0;
  unsigned int i=1;
  while(i <= total)
  {
    vCards0.push_back(i++);
  }

  vector<unsigned int> vCards1(vCards0);   //初始化另一个vector
  unsigned int idx=0;
  srand(time(NULL));

  i=0;
  unsigned int tt=total;
  while(i < total)  //依次抽取52张牌
  {
    idx = rand() % tt;  //从vCards0中随机抽取一张牌
    --tt;
    vCards1[i++]=vCards0[idx];  //放入vCards1

    //从vCards0中删除被抽取的那张牌

    vector<unsigned int>::iterator vi=vCards0.begin();
    unsigned int j=0;
    while(j < idx)
    {
      ++j;
      ++vi;
    }
    vCards0.erase(vi);
  }

  vector<unsigned int>::iterator vib=vCards1.begin();
  vector<unsigned int>::iterator vie=vCards1.end();
  while(vib != vie)
  {
    cout << *vib++ << " ";
  }
  cout << endl;

  return 0;
}

当然网上还有一些效率更高的算法,大家有兴趣可以找来看看。

分类: C/C++ 标签:

hiredis处理zscan和hscan的reply

2015年11月6日 没有评论

zscan的返回值可以看做是一个二维数组,第一维包含两个元素:string类型的游标cursor和集合元素数组;第二维即集合元素数组,这个数组交替存放着集合元素和score,元素和score也都是string类型的。当然所有的值都是通过指针来引用的,所以使用时务必小心。

当cursor为0时表示,扫描结束;非0的cursor值用来进行后续扫描。

集合元素数组reply->elements指示当前数组中包含多少个元素(指针),据此来遍历整个数组。

示例代码如下:

//cLocal points to the local redis

llCursor=0;

done=false;

while(!done)

{

cmd=”zscan “+setName+” %lld count 10″;
reply = (redisReply *)redisCommand(cLocal, cmd.c_str(), llCursor);
if(reply == NULL)
{
cout << “scan ” << setName << “failed, error is: ” << cLocal->errstr;
redisFree(cLocal);
cLocal = NULL;
break;
}

if(reply->type == REDIS_REPLY_ARRAY)
{
if(reply->elements == 0)
{
done=true;
cout << “get 0 msg from ” << setName;
}
else
{
llCursor=boost::lexical_cast<long long>(reply->element[0]->str);  //reply->element[0] contains cursor

redisReply ** siteCounters=reply->element[1]->element;               //reply->element[1] contains elements array:value1,score1,value2,score2,…
for(size_t i=0; i<reply->element[1]->elements; i++)
{
string elem = siteCounters[i++]->str;
string score = siteCounters[i]->str;

}
if(llCursor == 0)  //scan over
{
done=true;
}
}
}
else
{
done=true;
}
freeReplyObject(reply);

}

hscan和zscan api的用法一样。

分类: C/C++, Redis 标签: , , , , ,

gdb无法查看变量的解决办法

2015年9月22日 没有评论

gdb是调试c/c++程序的重要手段,为了在调试中看到变量的值,我们需要:

  • 编译时打开调试开关:-g
  • 不要使用任何优化选项:去掉所有的-O

通常情况下这样做就行了,可有时候还是会出现“No symbol foobar in current context”这样的问题,可能是编译器将变量存放在寄存器内导致的。

解决办法为

  • 设置调试输出格式:-gstabs+
  • 在64位机器上更好的输出格式选项:-gdwarf-2

另,通过print查看字符串变量的值时,如果字符串太长,默认只显示部分字符串。可以通过set print elements 0来显示完整的字符串。

分类: C/C++, 资料 标签: ,

c代码中执行shell命令的方法

2015年9月18日 没有评论
  1. 使用system
  2. 使用popen,可以获取shell的输出

FILE * fp = popen(cmd.c_str(), “r”);
if(fp == NULL)
{
cout << “popen failed to excute ” << cmd;
return 1;
}

 

string out;

while(!feof(fp))

{

char buffer[1024] = {0};

fgets(buffer, sizeof(buffer), fp);

out += buffer;

}
pclose(fp);
cout << cmd << “, output is: ” << out;

return 0;

分类: C/C++, 资料 标签: , ,

Hiredis: redis c client使用注记

2015年9月17日 没有评论
  1. 编译
  2. 使用
    • 初始化
    • 连接redis数据库

      redisContext * pConn = redisConnect(redisIp.c_str(), redisPort);
      if (m_cLocal == NULL)
      {
      return 1;
      }

      if(pConn->err)
      {
      cout << “Connection to redis[” << redisIp << “] error: ” << pConn->errstr;
      redisFree(pConn);
      return 2;
      }

    • 执行命令

      string cmd=”set foo bar;
      redisReply * reply=(redisReply *)redisCommand(pConn, cmd.c_str());
      if(reply == NULL)
      {
      cout << “redis[” << redisIp << “] excute ” << cmd << ” failed, error is: ” << pConn->errstr;
      redisFree(pConn);
      pConn=NULL;
      return 3;
      }
      freeReplyObject(reply);

    • 解析结果
      • 不同的命令返回不同类型的结果,需要根据reply->type字段进行解析
      • reply->type主要有以下6种,可嵌套,比如array类型的结构中包含指向其他array的指针

        #define REDIS_REPLY_STRING 1
        #define REDIS_REPLY_ARRAY 2
        #define REDIS_REPLY_INTEGER 3
        #define REDIS_REPLY_NIL 4
        #define REDIS_REPLY_STATUS 5
        #define REDIS_REPLY_ERROR 6

      • 由于返回结果通过指针来引用,操作时需要格外小心
  3. 注意事项
  • Once an error is returned the context cannot be reused and you should set up a new connection.

上面示例中执行命令时,如果返回了错误,则连接不可用,需要重新建立连接。

  • redisCommand这个函数的用法应参照c中的printf。上面示例中,如果cmd中含%,特别要引起注意!
分类: C/C++, Redis, 数据库, 资料 标签: , , , ,

MongoDB c++驱动安装与使用

2015年9月15日 1 条评论
  1. 安装
    • 获取源码:git clone https://github.com/mongodb/mongo-cxx-driver.git,解压
    • 安装编译工具scons:yum install -y scons
    • 编译:进入mongo-cxx-driver目录,执行:scons –prefix=/home/work/mongo/ –sharedclient install
    • 驱动已被安装在/home/work/mongo中
  2. 使用
    • 编译示例程序
      #include <cstdlib>
      #include <iostream>
      #include "mongo/client/dbclient.h" // for the driver
      
      void run() {
        mongo::DBClientConnection c;
        c.connect("localhost");
      }
      
      int main() {
          mongo::client::initialize();
          try {
              run();
              std::cout << "connected ok" << std::endl;
          } catch( const mongo::DBException &e ) {
              std::cout << "caught " << e.what() << std::endl;
          }
          return EXIT_SUCCESS;
      }
    • gcc tutorial.cpp -I./mongo/include -L./lib/ -L./mongo/lib -lmongoclient -Wl,-rpath=./lib/ -o tutorial
    • 上面的编译选项中,mongo、lib与tutorial.cpp位于同一目录,mongo即是第一步中指定的安装目录,lib存放其他依赖库,如boost。
    • 向数据库插入文档

      using mongo::BSONElement;
      using mongo::BSONObj;
      using mongo::BSONObjBuilder;

      BSONObjBuilder b;
      b.append(“Name”, “Liuyj”);
      b.append(“Age”, 30);
      BSONObj p = b.obj();

      try
      {
          c.insert(“Db.UserInfo”, p);
          string err = c.getLastError();
          if(!err.empty())
          {
              cout << “failed to insert into Db.UserInfo: ” << err;
              return 1;
          }
      }
      catch(const mongo::DBException &e)
      {
          cout << “exception occurred: ” << e.toString();

          return 2;
      }

  3. 遇到的问题
    • mongo::client::initialize()出现类似下面的崩溃
      Thread [1] 2560 [core: 2] (Suspended : Signal : SIGSEGV:Segmentation fault) 
              0x7fffffffdd80  
              ~_Function_base() at functional:2,030 0x448930  
              ~function() at functional:2,174 0x448930    
              mongo::client::Options::~Options() at options.h:32 0x448930 
              main() at main.cpp:17 0x43bf66
      stackoverflow的这个帖子给出了答案:应用程序和mongodb驱动使用了不一致的编码选项-std=c++11造成的。去掉这个选项就ok了。
    • 其他
  4. 其他
分类: C/C++, MongoDB, 数据库, 资料 标签: ,

gdb调试子进程

2015年9月10日 没有评论

我们用gdb调试程序通常有两种途径:

1.使用gdb启动程序,直接在里面加断点调试

2.通过gdb attach pid附着到运行的程序中

对于多进程的情况,如果子进程启动的比较快,运行gdb时,你想要观察的断点处已经被运行过,那么就很难调试子进程了。

gdb有个选项set follow-fork-mode child,可以方便的调试子进程。方法:

1.gdb启动程序

2.set follow-fork-mode child

3.加断点,run

新技能,Get it!

分类: C/C++, 资料 标签: