Commit 0d5e3bdf70652badb26bbe74ad59e949e3cd90fb
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.
Showing
7 changed files
with
156 additions
and
96 deletions
CMakeLists.txt
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<ReplyT>::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<ReplyT>::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& 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<class ReplyT> |
| 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<ReplyT>* Redox::command_blocking(const std::string& 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<ReplyT>* Redox::command_blocking(const std::string& 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; | ... | ... |