Commit 0d5e3bdf70652badb26bbe74ad59e949e3cd90fb

Authored by Hayk Martirosyan
1 parent dc17c42e

Add nullptr_t command type, refactor code even more

Added Command<nullptr_t> which successfully returns NIL
replies from Redis, with REDOX_OK. Also renamed constants
from REDISX_ -> REDOX_.

Moved a bunch of code from redox.cpp into command.cpp, where
it logically makes more sense.
CMakeLists.txt
... ... @@ -13,6 +13,7 @@ set(SRC_DIR ${CMAKE_SOURCE_DIR}/src)
13 13  
14 14 set(SRC_CORE
15 15 ${SRC_DIR}/redox.cpp
  16 + ${SRC_DIR}/command.cpp
16 17 )
17 18  
18 19 set(SRC_ALL ${SRC_CORE})
... ...
examples/simple_loop.cpp
... ... @@ -17,19 +17,21 @@ int main(int argc, char* argv[]) {
17 17  
18 18 Redox rdx = {"localhost", 6379};
19 19 rdx.run();
20   -//
21   -// Command<int>* del_cmd = rdx.command_blocking<int>("DEL simple_loop:count");
22   -// cout << "deleted key, reply: " << del_cmd->reply() << endl;
23   -// del_cmd->free();
  20 +
24 21 if(rdx.command_blocking("DEL simple_loop:count")) cout << "Deleted simple_loop:count" << endl;
25 22 else cerr << "Failed to delete simple_loop:count" << endl;
26 23  
  24 + Command<nullptr_t>* null_cmd = rdx.command_blocking<nullptr_t>("GET WFEOIEFJ");
  25 + if(null_cmd->status() == REDOX_OK) cout << "got nonexistent key." << endl;
  26 + else cerr << "error with null cmd: " << null_cmd->status() << endl;
  27 + null_cmd->free();
  28 +
27 29 Command<char*>* set_cmd = rdx.command_blocking<char*>("SET simple_loop:count 0");
28 30 cout << "set key, reply: " << set_cmd->reply() << endl;
29 31 set_cmd->free();
30 32  
31 33 Command<char*>* count_cmd = rdx.command_blocking<char*>("GET simple_loop:count");
32   - if(count_cmd->status() == REDISX_OK) {
  34 + if(count_cmd->status() == REDOX_OK) {
33 35 cout << "At the start, simple_loop:count = " << count_cmd->reply() << endl;
34 36 }
35 37 count_cmd->free();
... ... @@ -38,7 +40,7 @@ int main(int argc, char* argv[]) {
38 40  
39 41 double freq = 10000; // Hz
40 42 double dt = 1 / freq; // s
41   - double t = 1; // s
  43 + double t = 3; // s
42 44  
43 45 cout << "Running \"" << cmd_str << "\" at dt = " << dt
44 46 << "s for " << t << "s..." << endl;
... ...
examples/simple_sync_loop.cpp
... ... @@ -30,7 +30,7 @@ int main(int argc, char* argv[]) {
30 30  
31 31 for(int i = 0; i < count; i++) {
32 32 Command<int>* c = rdx.command_blocking<int>(cmd_str);
33   - if(c->status() != REDIS_OK) cerr << "Bad reply, code: " << c->status() << endl;
  33 + if(c->status() != REDOX_OK) cerr << "Bad reply, code: " << c->status() << endl;
34 34 }
35 35  
36 36 cout << "At the end, simple_loop:count = "
... ...
src/command.cpp 0 → 100644
  1 +/**
  2 +* Redis C++11 wrapper.
  3 +*/
  4 +
  5 +#include "command.hpp"
  6 +
  7 +namespace redox {
  8 +
  9 +template<class ReplyT>
  10 +bool Command<ReplyT>::is_error_reply() {
  11 +
  12 + if (reply_obj->type == REDIS_REPLY_ERROR) {
  13 + std::cerr << "[ERROR] " << cmd << ": " << reply_obj->str << std::endl;
  14 + return true;
  15 + }
  16 + return false;
  17 +}
  18 +
  19 +template<class ReplyT>
  20 +bool Command<ReplyT>::is_nil_reply() {
  21 +
  22 + if (reply_obj->type == REDIS_REPLY_NIL) {
  23 + std::cerr << "[WARNING] " << cmd << ": Nil reply." << std::endl;
  24 + return true;
  25 + }
  26 + return false;
  27 +}
  28 +
  29 +template<>
  30 +void Command<redisReply*>::invoke_callback() {
  31 + invoke(reply_obj);
  32 +}
  33 +
  34 +template<>
  35 +void Command<std::string>::invoke_callback() {
  36 +
  37 + if(is_error_reply()) invoke_error(REDOX_ERROR_REPLY);
  38 + else if(is_nil_reply()) invoke_error(REDOX_NIL_REPLY);
  39 +
  40 + else if(reply_obj->type != REDIS_REPLY_STRING && reply_obj->type != REDIS_REPLY_STATUS) {
  41 + std::cerr << "[ERROR] " << cmd << ": Received non-string reply." << std::endl;
  42 + invoke_error(REDOX_WRONG_TYPE);
  43 +
  44 + } else {
  45 + std::string s = reply_obj->str;
  46 + invoke(s);
  47 + }
  48 +}
  49 +
  50 +template<>
  51 +void Command<char*>::invoke_callback() {
  52 +
  53 + if(is_error_reply()) invoke_error(REDOX_ERROR_REPLY);
  54 + else if(is_nil_reply()) invoke_error(REDOX_NIL_REPLY);
  55 +
  56 + else if(reply_obj->type != REDIS_REPLY_STRING && reply_obj->type != REDIS_REPLY_STATUS) {
  57 + std::cerr << "[ERROR] " << cmd << ": Received non-string reply." << std::endl;
  58 + invoke_error(REDOX_WRONG_TYPE);
  59 +
  60 + } else {
  61 + invoke(reply_obj->str);
  62 + }
  63 +}
  64 +
  65 +template<>
  66 +void Command<int>::invoke_callback() {
  67 +
  68 + if(is_error_reply()) invoke_error(REDOX_ERROR_REPLY);
  69 + else if(is_nil_reply()) invoke_error(REDOX_NIL_REPLY);
  70 +
  71 + else if(reply_obj->type != REDIS_REPLY_INTEGER) {
  72 + std::cerr << "[ERROR] " << cmd << ": Received non-integer reply." << std::endl;
  73 + invoke_error(REDOX_WRONG_TYPE);
  74 +
  75 + } else {
  76 + invoke((int) reply_obj->integer);
  77 + }
  78 +}
  79 +
  80 +template<>
  81 +void Command<long long int>::invoke_callback() {
  82 +
  83 + if(is_error_reply()) invoke_error(REDOX_ERROR_REPLY);
  84 + else if(is_nil_reply()) invoke_error(REDOX_NIL_REPLY);
  85 +
  86 + else if(reply_obj->type != REDIS_REPLY_INTEGER) {
  87 + std::cerr << "[ERROR] " << cmd << ": Received non-integer reply." << std::endl;
  88 + invoke_error(REDOX_WRONG_TYPE);
  89 +
  90 + } else {
  91 + invoke(reply_obj->integer);
  92 + }
  93 +}
  94 +
  95 +template<>
  96 +void Command<std::nullptr_t>::invoke_callback() {
  97 +
  98 + if(is_error_reply()) invoke_error(REDOX_ERROR_REPLY);
  99 +
  100 + else if(reply_obj->type != REDIS_REPLY_NIL) {
  101 + std::cerr << "[ERROR] " << cmd << ": Received non-nil reply." << std::endl;
  102 + invoke_error(REDOX_WRONG_TYPE);
  103 +
  104 + } else {
  105 + invoke(nullptr);
  106 + }
  107 +}
  108 +
  109 +} // End namespace redox
... ...
src/command.hpp
... ... @@ -8,19 +8,20 @@
8 8 #include <string>
9 9 #include <functional>
10 10 #include <atomic>
  11 +#include <mutex>
11 12  
12 13 #include <hiredis/adapters/libev.h>
13 14 #include <hiredis/async.h>
14 15  
15 16 namespace redox {
16 17  
17   -static const int REDISX_UNINIT = -1;
18   -static const int REDISX_OK = 0;
19   -static const int REDISX_SEND_ERROR = 1;
20   -static const int REDISX_WRONG_TYPE = 2;
21   -static const int REDISX_NIL_REPLY = 3;
22   -static const int REDISX_ERROR_REPLY = 4;
23   -static const int REDISX_TIMEOUT = 5;
  18 +static const int REDOX_UNINIT = -1;
  19 +static const int REDOX_OK = 0;
  20 +static const int REDOX_SEND_ERROR = 1;
  21 +static const int REDOX_WRONG_TYPE = 2;
  22 +static const int REDOX_NIL_REPLY = 3;
  23 +static const int REDOX_ERROR_REPLY = 4;
  24 +static const int REDOX_TIMEOUT = 5;
24 25  
25 26 template<class ReplyT>
26 27 class Command {
... ... @@ -60,7 +61,7 @@ public:
60 61 */
61 62 void free();
62 63  
63   - void free_reply_object();
  64 + static void command_callback(redisAsyncContext *c, void *r, void *privdata);
64 65  
65 66 private:
66 67  
... ... @@ -81,6 +82,12 @@ private:
81 82 std::lock_guard<std::mutex> lg(timer_guard);
82 83 return &timer;
83 84 }
  85 +
  86 + void free_reply_object();
  87 +
  88 + void invoke_callback();
  89 + bool is_error_reply();
  90 + bool is_nil_reply();
84 91 };
85 92  
86 93 template<class ReplyT>
... ... @@ -97,6 +104,17 @@ Command&lt;ReplyT&gt;::Command(
97 104 }
98 105  
99 106 template<class ReplyT>
  107 +void Command<ReplyT>::command_callback(redisAsyncContext *c, void *r, void *privdata) {
  108 +
  109 + auto *cmd_obj = (Command<ReplyT> *) privdata;
  110 + cmd_obj->reply_obj = (redisReply *) r;
  111 + cmd_obj->invoke_callback();
  112 +
  113 + // Free the reply object unless told not to
  114 + if(cmd_obj->free_memory) cmd_obj->free_reply_object();
  115 +}
  116 +
  117 +template<class ReplyT>
100 118 void Command<ReplyT>::invoke(const ReplyT& r) {
101 119  
102 120 if(callback) callback(cmd, r);
... ... @@ -148,7 +166,7 @@ void Command&lt;ReplyT&gt;::free() {
148 166  
149 167 template<class ReplyT>
150 168 const ReplyT& Command<ReplyT>::reply() {
151   - if(reply_status != REDISX_OK) {
  169 + if(reply_status != REDOX_OK) {
152 170 std::cout << "[WARNING] " << cmd
153 171 << ": Accessing value of reply with status != OK." << std::endl;
154 172 }
... ...
src/redox.cpp
... ... @@ -98,34 +98,6 @@ void Redox::block() {
98 98 exit_waiter.wait(ul, [this]() { return to_exit.load(); });
99 99 }
100 100  
101   -template<class ReplyT>
102   -void invoke_callback(
103   - Command<ReplyT>* cmd_obj,
104   - redisReply* reply
105   -);
106   -
107   -template<class ReplyT>
108   -void Redox::command_callback(redisAsyncContext *c, void *r, void *privdata) {
109   -
110   - auto *cmd_obj = (Command<ReplyT> *) privdata;
111   - cmd_obj->reply_obj = (redisReply *) r;
112   -
113   - if (cmd_obj->reply_obj->type == REDIS_REPLY_ERROR) {
114   - std::cerr << "[ERROR redisx.hpp:121] " << cmd_obj->cmd << ": " << cmd_obj->reply_obj->str << std::endl;
115   - cmd_obj->invoke_error(REDISX_ERROR_REPLY);
116   -
117   - } else if(cmd_obj->reply_obj->type == REDIS_REPLY_NIL) {
118   - std::cerr << "[WARNING] " << cmd_obj->cmd << ": Nil reply." << std::endl;
119   - cmd_obj->invoke_error(REDISX_NIL_REPLY);
120   -
121   - } else {
122   - invoke_callback<ReplyT>(cmd_obj, cmd_obj->reply_obj);
123   - }
124   -
125   - // Free the reply object unless told not to
126   - if(cmd_obj->free_memory) cmd_obj->free_reply_object();
127   -}
128   -
129 101 /**
130 102 * Submit an asynchronous command to the Redox server. Return
131 103 * true if succeeded, false otherwise.
... ... @@ -133,9 +105,9 @@ void Redox::command_callback(redisAsyncContext *c, void *r, void *privdata) {
133 105 template<class ReplyT>
134 106 bool submit_to_server(Command<ReplyT>* cmd_obj) {
135 107 cmd_obj->pending++;
136   - if (redisAsyncCommand(cmd_obj->c, Redox::command_callback<ReplyT>, (void*)cmd_obj, cmd_obj->cmd.c_str()) != REDIS_OK) {
  108 + if (redisAsyncCommand(cmd_obj->c, cmd_obj->command_callback, (void*)cmd_obj, cmd_obj->cmd.c_str()) != REDIS_OK) {
137 109 cerr << "[ERROR] Could not send \"" << cmd_obj->cmd << "\": " << cmd_obj->c->errstr << endl;
138   - cmd_obj->invoke_error(REDISX_SEND_ERROR);
  110 + cmd_obj->invoke_error(REDOX_SEND_ERROR);
139 111 return false;
140 112 }
141 113 return true;
... ... @@ -191,6 +163,7 @@ void Redox::process_queued_commands() {
191 163 else if(process_queued_command<char*>(cmd_ptr)) {}
192 164 else if(process_queued_command<int>(cmd_ptr)) {}
193 165 else if(process_queued_command<long long int>(cmd_ptr)) {}
  166 + else if(process_queued_command<nullptr_t>(cmd_ptr)) {}
194 167 else throw runtime_error("[FATAL] Command pointer not found in any queue!");
195 168  
196 169 command_queue.pop();
... ... @@ -208,64 +181,20 @@ long Redox::num_commands_processed() {
208 181 template<> unordered_map<void*, Command<redisReply*>*>&
209 182 Redox::get_command_map() { return commands_redis_reply; }
210 183  
211   -template<>
212   -void invoke_callback(Command<redisReply*>* cmd_obj, redisReply* reply) {
213   - cmd_obj->invoke(reply);
214   -}
215   -
216 184 template<> unordered_map<void*, Command<string>*>&
217 185 Redox::get_command_map() { return commands_string_r; }
218 186  
219   -template<>
220   -void invoke_callback(Command<string>* cmd_obj, redisReply* reply) {
221   - if(reply->type != REDIS_REPLY_STRING && reply->type != REDIS_REPLY_STATUS) {
222   - cerr << "[ERROR] " << cmd_obj->cmd << ": Received non-string reply." << endl;
223   - cmd_obj->invoke_error(REDISX_WRONG_TYPE);
224   - return;
225   - }
226   -
227   - string s = reply->str;
228   - cmd_obj->invoke(s);
229   -}
230   -
231 187 template<> unordered_map<void*, Command<char*>*>&
232 188 Redox::get_command_map() { return commands_char_p; }
233 189  
234   -template<>
235   -void invoke_callback(Command<char*>* cmd_obj, redisReply* reply) {
236   - if(reply->type != REDIS_REPLY_STRING && reply->type != REDIS_REPLY_STATUS) {
237   - cerr << "[ERROR] " << cmd_obj->cmd << ": Received non-string reply." << endl;
238   - cmd_obj->invoke_error(REDISX_WRONG_TYPE);
239   - return;
240   - }
241   - cmd_obj->invoke(reply->str);
242   -}
243   -
244 190 template<> unordered_map<void*, Command<int>*>&
245 191 Redox::get_command_map() { return commands_int; }
246 192  
247   -template<>
248   -void invoke_callback(Command<int>* cmd_obj, redisReply* reply) {
249   - if(reply->type != REDIS_REPLY_INTEGER) {
250   - cerr << "[ERROR] " << cmd_obj->cmd << ": Received non-integer reply." << endl;
251   - cmd_obj->invoke_error(REDISX_WRONG_TYPE);
252   - return;
253   - }
254   - cmd_obj->invoke((int)reply->integer);
255   -}
256   -
257 193 template<> unordered_map<void*, Command<long long int>*>&
258 194 Redox::get_command_map() { return commands_long_long_int; }
259 195  
260   -template<>
261   -void invoke_callback(Command<long long int>* cmd_obj, redisReply* reply) {
262   - if(reply->type != REDIS_REPLY_INTEGER) {
263   - cerr << "[ERROR] " << cmd_obj->cmd << ": Received non-integer reply." << endl;
264   - cmd_obj->invoke_error(REDISX_WRONG_TYPE);
265   - return;
266   - }
267   - cmd_obj->invoke(reply->integer);
268   -}
  196 +template<> unordered_map<void*, Command<nullptr_t>*>&
  197 +Redox::get_command_map() { return commands_null; }
269 198  
270 199 // ----------------------------
271 200 // Helpers
... ... @@ -277,7 +206,7 @@ void Redox::command(const string&amp; cmd) {
277 206  
278 207 bool Redox::command_blocking(const string& cmd) {
279 208 Command<redisReply*>* c = command_blocking<redisReply*>(cmd);
280   - bool succeeded = (c->status() == REDISX_OK);
  209 + bool succeeded = (c->status() == REDOX_OK);
281 210 c->free();
282 211 return succeeded;
283 212 }
... ...
src/redox.hpp
... ... @@ -89,6 +89,7 @@ private:
89 89 std::unordered_map<void*, Command<char*>*> commands_char_p;
90 90 std::unordered_map<void*, Command<int>*> commands_int;
91 91 std::unordered_map<void*, Command<long long int>*> commands_long_long_int;
  92 + std::unordered_map<void*, Command<std::nullptr_t>*> commands_null;
92 93  
93 94 template<class ReplyT>
94 95 std::unordered_map<void*, Command<ReplyT>*>& get_command_map();
... ... @@ -142,7 +143,7 @@ template&lt;class ReplyT&gt;
142 143 Command<ReplyT>* Redox::command_blocking(const std::string& cmd) {
143 144  
144 145 ReplyT val;
145   - std::atomic_int status(REDISX_UNINIT);
  146 + std::atomic_int status(REDOX_UNINIT);
146 147  
147 148 std::condition_variable cv;
148 149 std::mutex m;
... ... @@ -153,7 +154,7 @@ Command&lt;ReplyT&gt;* Redox::command_blocking(const std::string&amp; cmd) {
153 154 [&val, &status, &m, &cv](const std::string& cmd_str, const ReplyT& reply) {
154 155 std::unique_lock<std::mutex> ul(m);
155 156 val = reply;
156   - status = REDISX_OK;
  157 + status = REDOX_OK;
157 158 ul.unlock();
158 159 cv.notify_one();
159 160 },
... ... @@ -167,7 +168,7 @@ Command&lt;ReplyT&gt;* Redox::command_blocking(const std::string&amp; cmd) {
167 168 );
168 169  
169 170 // Wait until a callback is invoked
170   - cv.wait(lk, [&status] { return status != REDISX_UNINIT; });
  171 + cv.wait(lk, [&status] { return status != REDOX_UNINIT; });
171 172  
172 173 cmd_obj->reply_val = val;
173 174 cmd_obj->reply_status = status;
... ...