c++ hiredis封装

/*
 * ----------------------------------------------------------------------------
 * Copyright (c) 2013-2014, xSky <guozhw at gmail dot com>
 * All rights reserved.
 * Distributed under GPL license.
 * ----------------------------------------------------------------------------
 */


#ifndef _XREDIS_CLIENT_H_
#define _XREDIS_CLIENT_H_
#include "hiredis.h"


#include <stdint.h>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "util.h"




using namespace std;


#define MAX_ERR_STR_LEN 128


typedef std::string             KEY;
typedef std::string             VALUE;
typedef std::vector<KEY>        KEYS;
typedef std::vector<VALUE>      VALUES;
typedef std::vector<string>     VDATA;


typedef std::set<string>        SETDATA;










typedef struct _REDIS_NODE_
{
    unsigned int dbindex;
    const char  *host;
    unsigned int port;
    const char  *passwd;
    unsigned int poolsize;
    unsigned int timeout;
}RedisNode;




class xRedisClient;




typedef struct _DATA_ITEM_
 {
    int    type;
    string str;
}DataItem;


typedef std::vector<DataItem>       ReplyData;
typedef ReplyData                   ArrayReply;
typedef std::map<string, double>    ZSETDATA;






typedef enum _BIT_OP_
{
    AND = 0,
    OR  = 1,
    XOR = 2,
    NOT = 3
}BITOP;


typedef enum _LIST_MODEL_
 {
    BEFORE = 0,
    AFTER  = 1
}LMODEL;






#define PING_REDIS_SEC       5


class xRedisClient
{
public:
    xRedisClient(string host, const unsigned int  port, string pass, const unsigned int  timeout )
    {
       mHost     = host;
       mPort     = port;
       mPass     = pass;
       mTimeout  = timeout;
       mLastTime = 0;
    }
    
    ~xRedisClient()
    {
      if(mCtx)
      {
        redisFree(mCtx);
      }


      if(mStrerr)
      {
        delete(mStrerr);
      }
      
    }


public:


    //              connection
    /* ECHO        */  
    /* PING        */  
    /* QUIT        */  
    /* SELECT      */  
    //                 Commands operating on string values
    /* DECR        */  bool decr(const string& key, int& result);
    /* DECRBY      */  bool decrby(const string& key, const int by, int& result);
    /* GET         */  bool get(const string& key,  string& value);
    /* GETBIT      */  bool getbit(const string& key,  const int& offset, int& bit);
    /* GETRANGE    */  bool getrange(const string& key,  const int start, const int end, string& out);
    /* GETSET      */  bool getset(const string& key,  const string& newValue, string& oldValue);
    /* INCR        */  bool incr(const string& key, int& result);
    /* INCRBY      */  bool incrby(const string& key, const int by, int& result);
    /* INCRBYFLOAT */  
    /* MGET        */  bool mget(const KEYS &  keys, ReplyData& vDdata);
    /* MSET        */  bool mset(const VDATA& data);
    /* MSETNX      */  
    /* SET         */  bool set(const string& key,  const string& value);
    /* SETBIT      */  bool setbit(const string& key,  const int offset, const int64_t newbitValue, int64_t oldbitValue);
    /* SETEX       */  bool setex(const string& key,  const int seconds, const string& value);
    /* SETNX       */  bool setnx(const string& key,  const string& value);
    /* SETRANGE    */  bool setrange(const string& key,  const int offset, const string& value, int& length);
    /* STRLEN      */ // bool strlen(const string& key, int& length);
    /* DEL          */  bool del(const string& key);
                bool del(const KEYS &  vkey, int64_t& count);
    /* DUMP         */
    /* EXISTS       */  bool exists( const string& key);
    /* RANDOMKEY    */  bool randomkey(  KEY& key);
    /* HDEL         */  bool hdel(const string& key, const string& filed, int64_t& num);
                        bool hdel(const string& key, const KEYS& vfiled, int64_t& num);
    /* HEXISTS      */  bool hexist(const string& key, const string& filed);
    /* HGET         */  bool hget(const string& key, const string& filed, string& value);
    /* HGETALL      */  bool hgetall(const string& key, ArrayReply& array);
    /* HKEYS        */  bool hkeys(const string& key, KEYS& keys);
    /* HLEN         */  bool hlen(const string& key, int64_t& count);
    /* HMGET        */  bool hmget(const string& key, const KEYS& filed, ArrayReply& array);
    /* HMSET        */  bool hmset(const string& key, const VDATA& vData);
    /* HSCAN        */                                     
    /* HSET         */  bool hset(const string& key, const string& filed, const string& value, int64_t& retval);
    /* HSETNX       */  bool hsetnx(const string& key, const string& filed, const string& value);
    /* HVALS        */  bool hvals(const string& key, VALUES& values);


    /* LLEN         */  bool llen(const string& key, int64_t& len);
    /* LPOP         */  bool lpop(const string& key, string& value);
    /* LPUSH        */  bool lpush(const string& key, const VALUES& vValue, int64_t& length);
    /* LPUSHX       */  bool lpushx(const string& key, const string& value, int64_t& length);
    /* LRANGE       */  bool lrange(const string& key, const int64_t start, const int64_t end, ArrayReply& array);
    /* LTRIM        */  bool ltrim(const string& key,  const int start, const int end);
    /* RPOP         */  bool rpop(const string& key, string& value);
    /* RPOPLPUSH    */  bool rpoplpush(const string& key_src, const string& key_dest, string& value);
    /* RPUSH        */  bool rpush(const string& key, const VALUES& vValue, int64_t& length);
    /* RPUSHX       */  bool rpushx(const string& key, const string& value, int64_t& length);
    /* SADD         */  bool sadd(const KEY& key, const VALUES& vValue, int64_t& count);
    /* SMOVE        */  bool smove(const KEY& srckey, const KEY& deskey,  const VALUE& member);
    /* SPOP         */  bool spop(const KEY& key, VALUE& member);
    /* SRANDMEMBER  */  bool srandmember(const KEY& key, VALUES& vmember, int num=0);
    /* ZADD             */ bool zadd(const KEY& deskey,   const VALUES& vValues, int64_t& count);
private:
    void addparam(VDATA& vDes, const VDATA& vSrc) 
    {
        for (VDATA::const_iterator iter=vSrc.begin(); iter!=vSrc.end();++iter) 
        {
            vDes.push_back(*iter);
        }
    }


    bool command_bool(const char* cmd, ...);
    bool command_status(const char* cmd, ...);
    bool command_integer(int64_t &intval,   const char* cmd, ...);
    bool command_string(string &data,      const char* cmd, ...);
    bool command_list(VALUES &vValue,    const char* cmd, ...);
    bool command_array(ArrayReply& array, const char* cmd, ...);
    bool commandargv_bool(const VDATA& vData);
    bool commandargv_status(const VDATA& vData);
    bool commandargv_array(const VDATA& vDataIn, ArrayReply& array);
    bool commandargv_array(const VDATA& vDataIn, VALUES& array);
    bool commandargv_integer(const VDATA& vDataIn, int64_t& retval);




public:
    char *GetErrInfo()
    {
     return this->mStrerr;
    }
    
    bool SetErrInfo(const char *info)
    {
        if (NULL==info) 
        {
            return false;
        }


        if (NULL == this->mStrerr)
        {
            this->mStrerr = new char[MAX_ERR_STR_LEN];
        }


        if (NULL!=this->mStrerr) 
        {
            memset(this->mStrerr,0, MAX_ERR_STR_LEN);
            strncpy(this->mStrerr, info, MAX_ERR_STR_LEN);
            return true;
        }
        return false;
    }






    bool CheckReply(const redisReply *reply)
    {
        if(NULL==reply) 
        {
            return false;
        }


        switch(reply->type)
        {
        case REDIS_REPLY_STRING:
        {
        return true;
        }
        case REDIS_REPLY_ARRAY:
        {
        return (strcasecmp(reply->str,"OK") == 0)?true:false;
        }
        case REDIS_REPLY_INTEGER:
        {
        return true;
        }
        case REDIS_REPLY_NIL:
        {
        return false;
        }
        case REDIS_REPLY_STATUS:
        {
        return (strcasecmp(reply->str,"OK") == 0)?true:false;
        }
        case REDIS_REPLY_ERROR:
        {
        return false;
        }
        default:
        {
        return false;
        }
        }


        return false;
    }




   
   
   void FreeReply(const redisReply *reply)
   {
       if (NULL!=reply) 
       {
           freeReplyObject((void*)reply);
       } 


   }


   
    bool RedisConnect() 
    {
     
      bool bRet = false;
      struct timeval timeoutVal;
      timeoutVal.tv_sec  = mTimeout;
      timeoutVal.tv_usec = 0;


      mCtx = redisConnectWithTimeout(mHost.c_str(), mPort, timeoutVal);
      if (NULL == mCtx || mCtx->err) 
      {
          if (NULL!=mCtx) 
          {
              redisFree(mCtx);
          } else
          {


          }
      } else
      {
          if (0 == mPass.size()) 
          {
              bRet = true;
          } else 
          {
              redisReply *reply = static_cast<redisReply *>(redisCommand(mCtx,"AUTH %s", mPass.c_str()));
              if((NULL==reply)||(strcasecmp(reply->str,"OK") != 0)) 
              {
                  bRet = false;
              }
              freeReplyObject(reply);
          }
      }


      return bRet;
    }




    bool Ping() const
    {
        redisReply *reply = static_cast<redisReply *>(redisCommand(mCtx,"PING"));
        bool bRet = (NULL!=reply);
        freeReplyObject(reply);
        return bRet;
    }




   
   //计算频率
    bool checkRedisNetwork()
    {
       time_t tick = time(NULL);
       if(tick >= mLastTime + PING_REDIS_SEC)
       {    
           if(Ping() == false)
           {
               DEBUG_LOG("检测redis断开");
               this->RedisConnect();
           }
           mLastTime = tick;
       }
    }


    redisContext  *getCtx() const     
    {
        return mCtx;
    }
     redisContext *mCtx;
private:
    char        *mStrerr;
    string  mHost;         // redis host
    unsigned int  mPort;          // redis sever port
    string mPass;         // redis server password
    unsigned int  mTimeout;       // connect timeout second
    time_t mLastTime;      //
};




#endif



/*
 * ----------------------------------------------------------------------------
 * Copyright (c) 2013-2014, xSky <guozhw at gmail dot com>
 * All rights reserved.
 * Distributed under GPL license.
 * ----------------------------------------------------------------------------
 */






#include "redis_client.h"
#include <sstream>


#include <stdlib.h>






bool xRedisClient::command_bool( const char *cmd, ...)
{
    bool bRet = false;




    va_list args;
    va_start(args, cmd);
    redisReply *reply = static_cast<redisReply *>(redisvCommand(mCtx, cmd, args));
    va_end(args);


    if (CheckReply(reply))
{
        bRet = (reply->integer==1)?true:false;
    } else 
    {
        SetErrInfo(reply->str);
    }


    FreeReply(reply);


    return bRet;
}




bool xRedisClient::command_status( const char* cmd, ...) 
{
    bool bRet = false;


    va_list args;
    va_start(args, cmd);
    redisReply *reply = static_cast<redisReply *>(redisvCommand(mCtx, cmd, args));
    va_end(args);


    if (CheckReply(reply)) 
{
        bRet = true;
    } else 
    {
        SetErrInfo(reply->str);
    }


    FreeReply(reply);
    return bRet;
}


bool xRedisClient::command_integer( int64_t &retval, const char* cmd, ...) 
{
    bool bRet = false;


if(this->mCtx == NULL)
{
return bRet;
}




    va_list args;
    va_start(args, cmd);
    redisReply *reply = static_cast<redisReply *>(redisvCommand(this->mCtx, cmd, args));
    va_end(args);

    if (CheckReply(reply)) 
{
        retval = reply->integer;
        bRet = true;
    } else 
    {
        SetErrInfo(reply->str);
    }


    FreeReply(reply);


    return bRet;
}


bool xRedisClient::command_string( string &data, const char* cmd, ...) 
{
    bool bRet = false;


if(this->mCtx == NULL)
{
return bRet;
}




    va_list args;
    va_start(args, cmd);
    redisReply *reply = static_cast<redisReply *>(redisvCommand(this->mCtx, cmd, args));
    va_end(args);
    if (CheckReply(reply)) 
{
        data = reply->str;
        bRet = true;
    } else 
    {
        SetErrInfo(reply->str);
    }


    FreeReply(reply);


    return bRet;
}


bool xRedisClient::command_list( VALUES &vValue, const char* cmd, ...) 
{
    bool bRet = false;
    va_list args;
    va_start(args, cmd);
    redisReply *reply = static_cast<redisReply *>(redisvCommand(this->mCtx, cmd, args));
    va_end(args);
    if (CheckReply(reply)) 
{
        for (unsigned int i =0; i<reply->elements; i++) 
{
            vValue.push_back(reply->element[i]->str);
        }
        bRet  = true;
    } else 
    {
         SetErrInfo(reply->str);
    }


    FreeReply(reply);


    return bRet;
}


bool xRedisClient::command_array(  ArrayReply& array,  const char* cmd, ...){


bool bRet = false;


    va_list args;
    va_start(args, cmd);
    redisReply *reply = static_cast<redisReply *>(redisvCommand(this->mCtx, cmd, args));
    va_end(args);
    if (CheckReply(reply)) 
{
        for (unsigned int i =0; i<reply->elements; i++) 
{
            DataItem item;
            item.type = reply->element[i]->type;
            item.str  = reply->element[i]->str;
            array.push_back(item);
        }
        bRet  = true;
    } else 
   {


        SetErrInfo(reply->str);
    }


    FreeReply(reply);
    return bRet;
}


bool xRedisClient::commandargv_bool( const VDATA& vData) {
    bool bRet = false;


    vector<const char *> argv( vData.size() );
    vector<size_t> argvlen( vData.size() );
    unsigned int j = 0;
    for ( vector<string>::const_iterator i = vData.begin(); i != vData.end(); ++i, ++j )
{
        argv[j] = i->c_str(), argvlen[j] = i->size();
    }


    redisReply *reply = static_cast<redisReply *>(redisCommandArgv(this->mCtx, argv.size(), &(argv[0]), &(argvlen[0])));
    if (CheckReply(reply)) 
{
        bRet = (reply->integer==1)?true:false;
    }
    FreeReply(reply);
    return bRet;
}


bool xRedisClient::commandargv_status( const VDATA& vData)
{
    bool bRet = false;
    vector<const char *> argv( vData.size() );
    vector<size_t> argvlen( vData.size() );
    unsigned int j = 0;
    for ( vector<string>::const_iterator i = vData.begin(); i != vData.end(); ++i, ++j )
{
        argv[j] = i->c_str(), argvlen[j] = i->size();
    }
    redisReply *reply = static_cast<redisReply *>(redisCommandArgv(this->mCtx, argv.size(), &(argv[0]), &(argvlen[0])));
    if (CheckReply(reply))
{
        bRet = true;
    }
    FreeReply(reply);
    return bRet;
}


bool xRedisClient::commandargv_array( const VDATA& vDataIn, ArrayReply& array)
{
    bool bRet = false;
    vector<const char*> argv( vDataIn.size() );
    vector<size_t> argvlen( vDataIn.size() );
    unsigned int j = 0;
    for ( vector<string>::const_iterator i = vDataIn.begin(); i != vDataIn.end(); ++i, ++j )
{
        argv[j] = i->c_str(), argvlen[j] = i->size();
    }
    redisReply *reply = static_cast<redisReply *>(redisCommandArgv(this->mCtx, argv.size(), &(argv[0]), &(argvlen[0])));
    if (CheckReply(reply))
{
        for (unsigned int i =0; i<reply->elements; i++) 
{
            DataItem item;
            item.type = reply->element[i]->type;
            item.str  = reply->element[i]->str;
            array.push_back(item);
        }
        bRet  = true;
    } else 
    {


        SetErrInfo(reply->str);
    }
    FreeReply(reply);
    return bRet;
}


bool xRedisClient::commandargv_array( const VDATA& vDataIn, VALUES& array)
{
    bool bRet = false;
    vector<const char*> argv( vDataIn.size() );
    vector<size_t> argvlen( vDataIn.size() );
    unsigned int j = 0;
    for ( vector<string>::const_iterator i = vDataIn.begin(); i != vDataIn.end(); ++i, ++j )
{
        argv[j] = i->c_str(), argvlen[j] = i->size();
    }
    redisReply *reply = static_cast<redisReply *>(redisCommandArgv(this->mCtx, argv.size(), &(argv[0]), &(argvlen[0])));
    if (CheckReply(reply))
{
        for (unsigned int i =0; i<reply->elements; i++) 
{
            array.push_back(reply->element[i]->str);
        }
        bRet  = true;
    } else 
    {
        SetErrInfo(reply->str);
    }


    FreeReply(reply);
   
    return bRet;
}


bool xRedisClient::commandargv_integer( const VDATA& vDataIn, int64_t& retval)
{
    bool bRet = false;




    vector<const char*> argv( vDataIn.size() );
    vector<size_t> argvlen( vDataIn.size() );
    unsigned int j = 0;
    for ( vector<string>::const_iterator iter = vDataIn.begin(); iter != vDataIn.end(); ++iter, ++j ) 
{
        argv[j] = iter->c_str(), argvlen[j] = iter->size();
    }


    redisReply *reply = static_cast<redisReply *>(redisCommandArgv(this->mCtx, argv.size(), &(argv[0]), &(argvlen[0])));
    if (CheckReply(reply)) 
{
        retval = reply->integer;
        bRet  = true;
    } else 
    {


        SetErrInfo(reply->str);
    }


    FreeReply(reply);


    return bRet;
}








bool xRedisClient::incr( const string& key, int& result) 
{
return command_integer((int64_t&)result, "INCR %s", key.c_str());
}




bool xRedisClient::set( const string& key, const  string& value) 
{
VDATA vCmdData;
vCmdData.push_back("SET");
vCmdData.push_back(key);
vCmdData.push_back(value);
return commandargv_status( vCmdData);
}




bool  xRedisClient::del(const string& key)
{
    return command_bool( "DEL %s", key.c_str());
}




bool xRedisClient::hdel(const string& key, const string& filed, int64_t& count) 
{
    return command_integer( count, "HDEL %s %s %s", key.c_str(), filed.c_str());
}


bool xRedisClient::hdel(const string& key, const KEYS& vfiled, int64_t& count) 
{
    VDATA vCmdData;
    vCmdData.push_back("HDEL");
    vCmdData.push_back(key);
    addparam(vCmdData, vfiled);
    return commandargv_integer(vCmdData, count);
}


bool xRedisClient::hexist(const string& key, const string& filed)
{
    return command_bool("HEXIST %s %s %s", key.c_str(), filed.c_str());
}


bool xRedisClient::hget(const string& key, const string& filed, string& value) 
{
    return command_string(value, "HGET %s %s %s", key.c_str(), filed.c_str());
}


bool  xRedisClient::hgetall(const string& key, ArrayReply& array)
{
    return command_array( array, "HGETALL %s", key.c_str());
}






bool xRedisClient::hkeys(const string& key, KEYS& keys)
{
    return command_list(keys, "HKEYS %s", key.c_str());
}


bool xRedisClient::hlen(const string& key, int64_t& count)
{
    return command_integer(count, "HLEN %s", key.c_str());
}




bool xRedisClient::hmget(const string& key, const KEYS& filed, ArrayReply& array)
{
    VDATA vCmdData;
    vCmdData.push_back("HMGET");
    vCmdData.push_back(key);
    addparam(vCmdData, filed);
    return commandargv_array(vCmdData, array);
}






bool xRedisClient::hmset(const string& key, const VDATA& vData)
{
    VDATA vCmdData;
    vCmdData.push_back("HMGET");
    vCmdData.push_back(key);
    addparam(vCmdData, vData);
    return commandargv_bool(vCmdData);
}


bool xRedisClient::hset(const string& key, const string& filed, const string& value, int64_t& retval)
{
    return command_integer( retval, "HSET %s %s %s", key.c_str(), filed.c_str(), value.c_str());
}






bool xRedisClient::hvals(const string& key, VALUES& values) 
{
    return command_list( values, "HVALS %s", key.c_str());
}


bool xRedisClient::exists(const string& key) 
{
    return command_bool( "EXISTS %s", key.c_str());
}




bool xRedisClient::randomkey( KEY& key)
{
    return command_string( key, "RANDOMKEY");
}








bool xRedisClient::llen(const string& key, int64_t& retval)
{
    return command_integer(retval, "LLEN %s", key.c_str());
}


bool xRedisClient::lpop(const string& key, string& value)
{
    return command_string(value, "LPOP %s", key.c_str());
}


bool xRedisClient::lpush(const string& key, const VALUES& vValue, int64_t& length)
{
    VDATA vCmdData;
    vCmdData.push_back("lpush");
    vCmdData.push_back(key);
    addparam(vCmdData, vValue);
    return commandargv_integer(vCmdData, length);
}


bool xRedisClient::lrange( const string& key, const int64_t start, const int64_t end, ArrayReply& array)
{
    return command_array(array, "LRANGE %s", key.c_str(), start, end);
}






bool xRedisClient::ltrim(const string& key, const int start, const int end)
{
    return command_status( "ltrim %s %d %d", key.c_str(), start, end);
}


bool xRedisClient::rpop(const string& key, string& value)
{
    return command_string(value, "LPOP %s", key.c_str());
}


bool xRedisClient::rpoplpush(const string& key_src, const string& key_dest, string& value)
{
    return command_string(value, "RPOPLPUSH %s %s", key_src.c_str(), key_dest.c_str());
}


bool xRedisClient::rpush(const string& key, const VALUES& vValue, int64_t& length)
{
    VDATA vCmdData;
    vCmdData.push_back("rpush");
    vCmdData.push_back(key);
    addparam(vCmdData, vValue);
    return commandargv_integer( vCmdData, length);
}


bool xRedisClient::rpushx( const string& key, const string& value, int64_t& length)
{
    return command_integer(length, "RPUSHX %s %s", key.c_str(), value.c_str());
}


//sets
bool xRedisClient::sadd( const string& key, const VALUES& vValue, int64_t& count)
{
    VDATA vCmdData;
    vCmdData.push_back("SADD");
    vCmdData.push_back(key);
    addparam(vCmdData, vValue);
    return commandargv_integer(vCmdData, count);
}




bool xRedisClient::smove(const KEY& srckey, const KEY& deskey,  const VALUE& member)
{
    return command_bool( "SMOVE %s", srckey.c_str(), deskey.c_str(), member.c_str());
}


bool xRedisClient::spop(const KEY& key, VALUE& member)
{
    return command_string(member, "SPOP %s", key.c_str());
}


bool xRedisClient::srandmember(const KEY& key, VALUES& members, int count)
{
    if (0==count) 
{
        return command_list( members, "SRANDMEMBER %s", key.c_str());
    }
    return command_list( members, "SRANDMEMBER %s %d", key.c_str(), count);
}




bool xRedisClient::zadd( const KEY& key,   const VALUES& vValues, int64_t& count)
{
    VDATA vCmdData;
    vCmdData.push_back("ZADD");
    vCmdData.push_back(key);
    addparam(vCmdData, vValues);
    return commandargv_integer( vCmdData, count);
}


 
 bool xRedisClient::setbit(const string& key,  const  int offset, const int64_t newbitValue, int64_t oldbitValue) 
 {
return command_integer(oldbitValue, "SETBIT %s %d %lld", key.c_str(), offset, newbitValue);
 }
 
 bool xRedisClient::get(const string& key,  string& value) 
 {
return command_string(value, "GET %s", key.c_str());
 }




 
 bool xRedisClient::getbit( const string& key, const int& offset, int& bit )
 {
return command_integer((int64_t&)bit, "GETBIT %s %d", key.c_str(), offset);
 }
 
 bool xRedisClient::getrange(const string& key,  const  int start, const int end, string& out) 
 {
return command_string(out, "GETRANGE %s %d %d", key.c_str(), start, end);
 }
 
 bool xRedisClient::getset(const string& key,  const string& newValue, string& oldValue)
 {
return command_string(oldValue, "GETSET %s %s", key.c_str(), newValue.c_str());
 }
 




 bool xRedisClient::setrange(const string& key,  const int offset, const string& value, int& length) 
 {
return command_integer((int64_t&)length, "setrange %s %d %s", key. c_str(), offset, value.c_str());
 }




 


 bool xRedisClient::incrby(const string& key, const int by, int& result)
 {
return command_integer((int64_t&)result, "INCR %s %d", key.c_str(), by);
 }






 
bool xRedisClient::decr( const string& key, int& result)
{
 return command_integer((int64_t&)result,"decr %s", key.c_str());
}
 
 bool xRedisClient::decrby( const string& key, const int by, int& result)
 {
return command_integer((int64_t&)result, "decrby %s %d", key.c_str(), by);
 }





















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值