Commit 518a29c131b9723bbb97f59bcb6d7407227b5a33

Authored by Hayk Martirosyan
1 parent 583c6fe3

Initial split of redisx from robot-net repository

Basic CMake setup, redisx implementation, and one example program.
.gitignore
... ... @@ -26,3 +26,9 @@
26 26 *.exe
27 27 *.out
28 28 *.app
  29 +
  30 +# CMake
  31 +build/
  32 +
  33 +# IntelliJ
  34 +.idea/
... ...
CMakeLists.txt 0 โ†’ 100644
  1 +cmake_minimum_required(VERSION 2.8.4)
  2 +project(redisx)
  3 +
  4 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fPIC -Wall")
  5 +# set(CMAKE_VERBOSE_MAKEFILE ON)
  6 +
  7 +# ---------------------------------------------------------
  8 +# Source files
  9 +# ---------------------------------------------------------
  10 +
  11 +set(SRC_DIR ${CMAKE_SOURCE_DIR}/src)
  12 +
  13 +set(SRC_CORE
  14 + ${SRC_DIR}/redisx.cpp
  15 +)
  16 +
  17 +set(SRC_ALL ${SRC_CORE})
  18 +
  19 +# ---------------------------------------------------------
  20 +# Libraries
  21 +# ---------------------------------------------------------
  22 +
  23 +set(LIB_REDIS hiredis event)
  24 +set(LIB_ALL ${LIB_REDIS})
  25 +
  26 +# ---------------------------------------------------------
  27 +# Examples
  28 +# ---------------------------------------------------------
  29 +
  30 +add_executable(basic examples/basic.cpp ${SRC_ALL})
  31 +target_link_libraries(basic ${LIB_REDIS})
... ...
... ... @@ -186,7 +186,7 @@ Apache License
186 186 same "printed page" as the copyright notice for easier
187 187 identification within third-party archives.
188 188  
189   - Copyright {yyyy} {name of copyright owner}
  189 + Copyright 2015 Hayk Martirosyan
190 190  
191 191 Licensed under the Apache License, Version 2.0 (the "License");
192 192 you may not use this file except in compliance with the License.
... ...
examples/basic.cpp 0 โ†’ 100644
  1 +/**
  2 +* Basic asynchronous calls using redisx.
  3 +*/
  4 +
  5 +#include <iostream>
  6 +#include <thread>
  7 +#include "../src/redisx.hpp"
  8 +
  9 +using namespace std;
  10 +
  11 +static const string REDIS_HOST = "localhost";
  12 +static const int REDIS_PORT = 6379;
  13 +
  14 +unsigned long time_ms() {
  15 + return chrono::system_clock::now().time_since_epoch()
  16 + /chrono::milliseconds(1);
  17 +}
  18 +
  19 +int main(int argc, char* argv[]) {
  20 +
  21 + redisx::Redis r(REDIS_HOST, REDIS_PORT);
  22 +
  23 + r.command<const string&>("GET blah", [](const string& cmd, const string& value) {
  24 + cout << "[COMMAND] " << cmd << ": " << value << endl;
  25 + });
  26 +
  27 + r.command<const char*>("GET blah", [](const string& cmd, const char* value) {
  28 + cout << "[COMMAND] " << cmd << ": " << value << endl;
  29 + });
  30 +
  31 + r.command<const redisReply*>("LPUSH yahoo 1 2 3 4 f w", [](const string& cmd, const redisReply* reply) {
  32 + cout << "[COMMAND] " << cmd << ": " << reply->integer << endl;
  33 + });
  34 +
  35 + r.get("blahqwefwqefef", [](const string& cmd, const char* value) {
  36 + cout << "[GET] blah: " << value << endl;
  37 + });
  38 +
  39 + r.set("name", "lolfewef");
  40 +
  41 + r.command("SET blah wefoijewfojiwef");
  42 +
  43 + r.del("name");
  44 + r.del("wefoipjweojiqw", [](const string& cmd, long long int num_deleted) {
  45 + cout << "num deleted: " << num_deleted << endl;
  46 + });
  47 +
  48 + unsigned long t0 = time_ms();
  49 + unsigned long t1 = t0;
  50 +
  51 + int len = 1000000;
  52 + int count = 0;
  53 +
  54 + for(int i = 0; i < len; i++) {
  55 + r.command<const string&>("set blah wefoiwef", [&t0, &t1, &count, len](const string& cmd, const string& reply) {
  56 +
  57 + count++;
  58 + if(count == len) {
  59 + cout << cmd << ": " << reply << endl;
  60 + cout << "Time to queue async commands: " << t1 - t0 << "ms" << endl;
  61 + cout << "Time to receive all: " << time_ms() - t1 << "ms" << endl;
  62 + cout << "Total time: " << time_ms() - t0 << "ms" << endl;
  63 + }
  64 + });
  65 + }
  66 + t1 = time_ms();
  67 +
  68 + thread loop([&r] { r.start(); });
  69 + loop.join();
  70 +
  71 + return 0;
  72 +}
... ...
make.sh 0 โ†’ 100755
  1 +mkdir -p build &&
  2 +cd build &&
  3 +cmake .. &&
  4 +time make &&
  5 +cd ..
... ...
src/redisx.cpp 0 โ†’ 100644
  1 +/**
  2 +* Redis C++11 wrapper.
  3 +*/
  4 +
  5 +#include <signal.h>
  6 +#include <iostream>
  7 +#include <thread>
  8 +#include <hiredis/adapters/libevent.h>
  9 +#include <vector>
  10 +#include <string.h>
  11 +#include "redisx.hpp"
  12 +
  13 +using namespace std;
  14 +
  15 +namespace redisx {
  16 +
  17 +void connected(const redisAsyncContext *c, int status) {
  18 + if (status != REDIS_OK) {
  19 + printf("Error: %s\n", c->errstr);
  20 + return;
  21 + }
  22 + printf("Connected...\n");
  23 +}
  24 +
  25 +void disconnected(const redisAsyncContext *c, int status) {
  26 + if (status != REDIS_OK) {
  27 + printf("Error: %s\n", c->errstr);
  28 + return;
  29 + }
  30 + printf("Disconnected...\n");
  31 +}
  32 +
  33 +Redis::Redis(const string& host, const int port) : host(host), port(port), io_ops(0) {
  34 +
  35 + signal(SIGPIPE, SIG_IGN);
  36 + base = event_base_new();
  37 +
  38 + c = redisAsyncConnect(host.c_str(), port);
  39 + if (c->err) {
  40 + printf("Error: %s\n", c->errstr);
  41 + return;
  42 + }
  43 +
  44 + redisLibeventAttach(c, base);
  45 + redisAsyncSetConnectCallback(c, connected);
  46 + redisAsyncSetDisconnectCallback(c, disconnected);
  47 +}
  48 +
  49 +Redis::~Redis() {
  50 + redisAsyncDisconnect(c);
  51 +}
  52 +
  53 +void Redis::start() {
  54 + event_base_dispatch(base);
  55 +}
  56 +
  57 +void Redis::command(const char* cmd) {
  58 + int status = redisAsyncCommand(c, NULL, NULL, cmd);
  59 + if (status != REDIS_OK) {
  60 + cerr << "[ERROR] Async command \"" << cmd << "\": " << c->errstr << endl;
  61 + return;
  62 + }
  63 +}
  64 +
  65 +// ----------------------------
  66 +
  67 +template<>
  68 +void invoke_callback(const CommandAsync<const redisReply*>* cmd_obj, redisReply* reply) {
  69 + cmd_obj->invoke(reply);
  70 +}
  71 +
  72 +template<>
  73 +void invoke_callback(const CommandAsync<const string&>* cmd_obj, redisReply* reply) {
  74 + if(reply->type != REDIS_REPLY_STRING && reply->type != REDIS_REPLY_STATUS) {
  75 + cerr << "[ERROR] " << cmd_obj->cmd << ": Received non-string reply." << endl;
  76 + return;
  77 + }
  78 +
  79 + cmd_obj->invoke(reply->str);
  80 +}
  81 +
  82 +template<>
  83 +void invoke_callback(const CommandAsync<const char*>* cmd_obj, redisReply* reply) {
  84 + if(reply->type != REDIS_REPLY_STRING && reply->type != REDIS_REPLY_STATUS) {
  85 + cerr << "[ERROR] " << cmd_obj->cmd << ": Received non-string reply." << endl;
  86 + return;
  87 + }
  88 + cmd_obj->invoke(reply->str);
  89 +}
  90 +
  91 +template<>
  92 +void invoke_callback(const CommandAsync<int>* cmd_obj, redisReply* reply) {
  93 + if(reply->type != REDIS_REPLY_INTEGER) {
  94 + cerr << "[ERROR] " << cmd_obj->cmd << ": Received non-integer reply." << endl;
  95 + return;
  96 + }
  97 + cmd_obj->invoke((int)reply->integer);
  98 +}
  99 +
  100 +template<>
  101 +void invoke_callback(const CommandAsync<long long int>* cmd_obj, redisReply* reply) {
  102 + if(reply->type != REDIS_REPLY_INTEGER) {
  103 + cerr << "[ERROR] " << cmd_obj->cmd << ": Received non-integer reply." << endl;
  104 + return;
  105 + }
  106 + cmd_obj->invoke(reply->integer);
  107 +}
  108 +
  109 +// ----------------------------
  110 +
  111 +void Redis::get(const char* key, function<void(const string&, const char*)> callback) {
  112 + string cmd = string("GET ") + key;
  113 + command<const char*>(cmd.c_str(), callback);
  114 +}
  115 +
  116 +void Redis::set(const char* key, const char* value) {
  117 + string cmd = string("SET ") + key + " " + value;
  118 + command<const char*>(cmd.c_str(), [](const string& command, const char* reply) {
  119 + if(strcmp(reply, "OK"))
  120 + cerr << "[ERROR] " << command << ": SET failed with reply " << reply << endl;
  121 + });
  122 +}
  123 +
  124 +void Redis::set(const char* key, const char* value, std::function<void(const string&, const char*)> callback) {
  125 + string cmd = string("SET ") + key + " " + value;
  126 + command<const char*>(cmd.c_str(), callback);
  127 +}
  128 +
  129 +void Redis::del(const char* key) {
  130 + string cmd = string("DEL ") + key;
  131 + command<long long int>(cmd.c_str(), [](const string& command, long long int num_deleted) {
  132 + if(num_deleted != 1)
  133 + cerr << "[ERROR] " << command << ": Deleted " << num_deleted << " keys." << endl;
  134 + });
  135 +}
  136 +
  137 +void Redis::del(const char* key, std::function<void(const string&, long long int)> callback) {
  138 + string cmd = string("DEL ") + key;
  139 + command<long long int>(cmd.c_str(), callback);
  140 +}
  141 +
  142 +} // End namespace redis
... ...
src/redisx.hpp 0 โ†’ 100644
  1 +/**
  2 +* Redis C++11 wrapper.
  3 +*/
  4 +
  5 +#pragma once
  6 +
  7 +#include <functional>
  8 +#include <string>
  9 +#include <iostream>
  10 +#include <hiredis/hiredis.h>
  11 +#include <hiredis/async.h>
  12 +
  13 +namespace redisx {
  14 +
  15 +class Redis {
  16 +
  17 +public:
  18 +
  19 + Redis(const std::string& host, const int port);
  20 + ~Redis();
  21 +
  22 + void start();
  23 +
  24 + template<class ReplyT>
  25 + void command(
  26 + const std::string& cmd,
  27 + const std::function<void(const std::string&, ReplyT)>& callback
  28 + );
  29 +
  30 + void command(const char* command);
  31 +
  32 + void get(const char* key, std::function<void(const std::string&, const char*)> callback);
  33 +
  34 + void set(const char* key, const char* value);
  35 + void set(const char* key, const char* value, std::function<void(const std::string&, const char*)> callback);
  36 +
  37 + void del(const char* key);
  38 + void del(const char* key, std::function<void(const std::string&, long long int)> callback);
  39 +
  40 +// void publish(std::string channel, std::string msg);
  41 +// void subscribe(std::string channel, std::function<void(std::string channel, std::string msg)> callback);
  42 +// void unsubscribe(std::string channel);
  43 +
  44 +private:
  45 +
  46 + // Redis server
  47 + std::string host;
  48 + int port;
  49 +
  50 + // Number of IOs performed
  51 + long io_ops;
  52 +
  53 + struct event_base *base;
  54 + redisAsyncContext *c;
  55 +};
  56 +
  57 +template<class ReplyT>
  58 +class CommandAsync {
  59 +public:
  60 + CommandAsync(const std::string& cmd, const std::function<void(const std::string&, ReplyT)>& callback)
  61 + : cmd(cmd), callback(callback) {}
  62 + const std::string cmd;
  63 + const std::function<void(const std::string&, ReplyT)> callback;
  64 + void invoke(ReplyT reply) const {if(callback != NULL) callback(cmd, reply); }
  65 +};
  66 +
  67 +template<class ReplyT>
  68 +void invoke_callback(
  69 + const CommandAsync<ReplyT>* cmd_obj,
  70 + redisReply* reply
  71 +);
  72 +
  73 +template<class ReplyT>
  74 +void command_callback(redisAsyncContext *c, void *r, void *privdata) {
  75 +
  76 + redisReply *reply = (redisReply *) r;
  77 + auto *cmd_obj = (CommandAsync<ReplyT> *) privdata;
  78 +
  79 + if (reply->type == REDIS_REPLY_ERROR) {
  80 + std::cerr << "[ERROR] " << cmd_obj->cmd << ": " << reply->str << std::endl;
  81 + delete cmd_obj;
  82 + return;
  83 + }
  84 +
  85 + if(reply->type == REDIS_REPLY_NIL) {
  86 + std::cerr << "[ERROR] " << cmd_obj->cmd << ": Nil reply." << std::endl;
  87 + delete cmd_obj;
  88 + return; // cmd_obj->invoke(NULL);
  89 + }
  90 +
  91 + invoke_callback<ReplyT>(cmd_obj, reply);
  92 + delete cmd_obj;
  93 +}
  94 +
  95 +template<class ReplyT>
  96 +void Redis::command(const std::string& cmd, const std::function<void(const std::string&, ReplyT)>& callback) {
  97 +
  98 + auto *cmd_obj = new CommandAsync<ReplyT>(cmd, callback);
  99 +
  100 + int status = redisAsyncCommand(c, command_callback<ReplyT>, (void*)cmd_obj, cmd.c_str());
  101 + if (status != REDIS_OK) {
  102 + std::cerr << "[ERROR] Async command \"" << cmd << "\": " << c->errstr << std::endl;
  103 + delete cmd_obj;
  104 + return;
  105 + }
  106 +}
  107 +
  108 +} // End namespace redis
... ...