diff --git a/CMakeLists.txt b/CMakeLists.txt index b782441..d31d8ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ set(SRC_DIR ${CMAKE_SOURCE_DIR}/src) set(SRC_CORE ${SRC_DIR}/redox.cpp + ${SRC_DIR}/command.cpp ) set(SRC_ALL ${SRC_CORE}) diff --git a/examples/simple_loop.cpp b/examples/simple_loop.cpp index 8a3a9ce..9c2523d 100644 --- a/examples/simple_loop.cpp +++ b/examples/simple_loop.cpp @@ -17,19 +17,21 @@ int main(int argc, char* argv[]) { Redox rdx = {"localhost", 6379}; rdx.run(); -// -// Command* del_cmd = rdx.command_blocking("DEL simple_loop:count"); -// cout << "deleted key, reply: " << del_cmd->reply() << endl; -// del_cmd->free(); + if(rdx.command_blocking("DEL simple_loop:count")) cout << "Deleted simple_loop:count" << endl; else cerr << "Failed to delete simple_loop:count" << endl; + Command* null_cmd = rdx.command_blocking("GET WFEOIEFJ"); + if(null_cmd->status() == REDOX_OK) cout << "got nonexistent key." << endl; + else cerr << "error with null cmd: " << null_cmd->status() << endl; + null_cmd->free(); + Command* set_cmd = rdx.command_blocking("SET simple_loop:count 0"); cout << "set key, reply: " << set_cmd->reply() << endl; set_cmd->free(); Command* count_cmd = rdx.command_blocking("GET simple_loop:count"); - if(count_cmd->status() == REDISX_OK) { + if(count_cmd->status() == REDOX_OK) { cout << "At the start, simple_loop:count = " << count_cmd->reply() << endl; } count_cmd->free(); @@ -38,7 +40,7 @@ int main(int argc, char* argv[]) { double freq = 10000; // Hz double dt = 1 / freq; // s - double t = 1; // s + double t = 3; // s cout << "Running \"" << cmd_str << "\" at dt = " << dt << "s for " << t << "s..." << endl; diff --git a/examples/simple_sync_loop.cpp b/examples/simple_sync_loop.cpp index 595c785..5a08b8a 100644 --- a/examples/simple_sync_loop.cpp +++ b/examples/simple_sync_loop.cpp @@ -30,7 +30,7 @@ int main(int argc, char* argv[]) { for(int i = 0; i < count; i++) { Command* c = rdx.command_blocking(cmd_str); - if(c->status() != REDIS_OK) cerr << "Bad reply, code: " << c->status() << endl; + if(c->status() != REDOX_OK) cerr << "Bad reply, code: " << c->status() << endl; } cout << "At the end, simple_loop:count = " diff --git a/src/command.cpp b/src/command.cpp new file mode 100644 index 0000000..d83851c --- /dev/null +++ b/src/command.cpp @@ -0,0 +1,109 @@ +/** +* Redis C++11 wrapper. +*/ + +#include "command.hpp" + +namespace redox { + +template +bool Command::is_error_reply() { + + if (reply_obj->type == REDIS_REPLY_ERROR) { + std::cerr << "[ERROR] " << cmd << ": " << reply_obj->str << std::endl; + return true; + } + return false; +} + +template +bool Command::is_nil_reply() { + + if (reply_obj->type == REDIS_REPLY_NIL) { + std::cerr << "[WARNING] " << cmd << ": Nil reply." << std::endl; + return true; + } + return false; +} + +template<> +void Command::invoke_callback() { + invoke(reply_obj); +} + +template<> +void Command::invoke_callback() { + + if(is_error_reply()) invoke_error(REDOX_ERROR_REPLY); + else if(is_nil_reply()) invoke_error(REDOX_NIL_REPLY); + + else if(reply_obj->type != REDIS_REPLY_STRING && reply_obj->type != REDIS_REPLY_STATUS) { + std::cerr << "[ERROR] " << cmd << ": Received non-string reply." << std::endl; + invoke_error(REDOX_WRONG_TYPE); + + } else { + std::string s = reply_obj->str; + invoke(s); + } +} + +template<> +void Command::invoke_callback() { + + if(is_error_reply()) invoke_error(REDOX_ERROR_REPLY); + else if(is_nil_reply()) invoke_error(REDOX_NIL_REPLY); + + else if(reply_obj->type != REDIS_REPLY_STRING && reply_obj->type != REDIS_REPLY_STATUS) { + std::cerr << "[ERROR] " << cmd << ": Received non-string reply." << std::endl; + invoke_error(REDOX_WRONG_TYPE); + + } else { + invoke(reply_obj->str); + } +} + +template<> +void Command::invoke_callback() { + + if(is_error_reply()) invoke_error(REDOX_ERROR_REPLY); + else if(is_nil_reply()) invoke_error(REDOX_NIL_REPLY); + + else if(reply_obj->type != REDIS_REPLY_INTEGER) { + std::cerr << "[ERROR] " << cmd << ": Received non-integer reply." << std::endl; + invoke_error(REDOX_WRONG_TYPE); + + } else { + invoke((int) reply_obj->integer); + } +} + +template<> +void Command::invoke_callback() { + + if(is_error_reply()) invoke_error(REDOX_ERROR_REPLY); + else if(is_nil_reply()) invoke_error(REDOX_NIL_REPLY); + + else if(reply_obj->type != REDIS_REPLY_INTEGER) { + std::cerr << "[ERROR] " << cmd << ": Received non-integer reply." << std::endl; + invoke_error(REDOX_WRONG_TYPE); + + } else { + invoke(reply_obj->integer); + } +} + +template<> +void Command::invoke_callback() { + + if(is_error_reply()) invoke_error(REDOX_ERROR_REPLY); + + else if(reply_obj->type != REDIS_REPLY_NIL) { + std::cerr << "[ERROR] " << cmd << ": Received non-nil reply." << std::endl; + invoke_error(REDOX_WRONG_TYPE); + + } else { + invoke(nullptr); + } +} + +} // End namespace redox diff --git a/src/command.hpp b/src/command.hpp index 7710d48..36fb9d0 100644 --- a/src/command.hpp +++ b/src/command.hpp @@ -8,19 +8,20 @@ #include #include #include +#include #include #include namespace redox { -static const int REDISX_UNINIT = -1; -static const int REDISX_OK = 0; -static const int REDISX_SEND_ERROR = 1; -static const int REDISX_WRONG_TYPE = 2; -static const int REDISX_NIL_REPLY = 3; -static const int REDISX_ERROR_REPLY = 4; -static const int REDISX_TIMEOUT = 5; +static const int REDOX_UNINIT = -1; +static const int REDOX_OK = 0; +static const int REDOX_SEND_ERROR = 1; +static const int REDOX_WRONG_TYPE = 2; +static const int REDOX_NIL_REPLY = 3; +static const int REDOX_ERROR_REPLY = 4; +static const int REDOX_TIMEOUT = 5; template class Command { @@ -60,7 +61,7 @@ public: */ void free(); - void free_reply_object(); + static void command_callback(redisAsyncContext *c, void *r, void *privdata); private: @@ -81,6 +82,12 @@ private: std::lock_guard lg(timer_guard); return &timer; } + + void free_reply_object(); + + void invoke_callback(); + bool is_error_reply(); + bool is_nil_reply(); }; template @@ -97,6 +104,17 @@ Command::Command( } template +void Command::command_callback(redisAsyncContext *c, void *r, void *privdata) { + + auto *cmd_obj = (Command *) privdata; + cmd_obj->reply_obj = (redisReply *) r; + cmd_obj->invoke_callback(); + + // Free the reply object unless told not to + if(cmd_obj->free_memory) cmd_obj->free_reply_object(); +} + +template void Command::invoke(const ReplyT& r) { if(callback) callback(cmd, r); @@ -148,7 +166,7 @@ void Command::free() { template const ReplyT& Command::reply() { - if(reply_status != REDISX_OK) { + if(reply_status != REDOX_OK) { std::cout << "[WARNING] " << cmd << ": Accessing value of reply with status != OK." << std::endl; } diff --git a/src/redox.cpp b/src/redox.cpp index aa60bcc..5994cdc 100644 --- a/src/redox.cpp +++ b/src/redox.cpp @@ -98,34 +98,6 @@ void Redox::block() { exit_waiter.wait(ul, [this]() { return to_exit.load(); }); } -template -void invoke_callback( - Command* cmd_obj, - redisReply* reply -); - -template -void Redox::command_callback(redisAsyncContext *c, void *r, void *privdata) { - - auto *cmd_obj = (Command *) privdata; - cmd_obj->reply_obj = (redisReply *) r; - - if (cmd_obj->reply_obj->type == REDIS_REPLY_ERROR) { - std::cerr << "[ERROR redisx.hpp:121] " << cmd_obj->cmd << ": " << cmd_obj->reply_obj->str << std::endl; - cmd_obj->invoke_error(REDISX_ERROR_REPLY); - - } else if(cmd_obj->reply_obj->type == REDIS_REPLY_NIL) { - std::cerr << "[WARNING] " << cmd_obj->cmd << ": Nil reply." << std::endl; - cmd_obj->invoke_error(REDISX_NIL_REPLY); - - } else { - invoke_callback(cmd_obj, cmd_obj->reply_obj); - } - - // Free the reply object unless told not to - if(cmd_obj->free_memory) cmd_obj->free_reply_object(); -} - /** * Submit an asynchronous command to the Redox server. Return * true if succeeded, false otherwise. @@ -133,9 +105,9 @@ void Redox::command_callback(redisAsyncContext *c, void *r, void *privdata) { template bool submit_to_server(Command* cmd_obj) { cmd_obj->pending++; - if (redisAsyncCommand(cmd_obj->c, Redox::command_callback, (void*)cmd_obj, cmd_obj->cmd.c_str()) != REDIS_OK) { + if (redisAsyncCommand(cmd_obj->c, cmd_obj->command_callback, (void*)cmd_obj, cmd_obj->cmd.c_str()) != REDIS_OK) { cerr << "[ERROR] Could not send \"" << cmd_obj->cmd << "\": " << cmd_obj->c->errstr << endl; - cmd_obj->invoke_error(REDISX_SEND_ERROR); + cmd_obj->invoke_error(REDOX_SEND_ERROR); return false; } return true; @@ -191,6 +163,7 @@ void Redox::process_queued_commands() { else if(process_queued_command(cmd_ptr)) {} else if(process_queued_command(cmd_ptr)) {} else if(process_queued_command(cmd_ptr)) {} + else if(process_queued_command(cmd_ptr)) {} else throw runtime_error("[FATAL] Command pointer not found in any queue!"); command_queue.pop(); @@ -208,64 +181,20 @@ long Redox::num_commands_processed() { template<> unordered_map*>& Redox::get_command_map() { return commands_redis_reply; } -template<> -void invoke_callback(Command* cmd_obj, redisReply* reply) { - cmd_obj->invoke(reply); -} - template<> unordered_map*>& Redox::get_command_map() { return commands_string_r; } -template<> -void invoke_callback(Command* cmd_obj, redisReply* reply) { - if(reply->type != REDIS_REPLY_STRING && reply->type != REDIS_REPLY_STATUS) { - cerr << "[ERROR] " << cmd_obj->cmd << ": Received non-string reply." << endl; - cmd_obj->invoke_error(REDISX_WRONG_TYPE); - return; - } - - string s = reply->str; - cmd_obj->invoke(s); -} - template<> unordered_map*>& Redox::get_command_map() { return commands_char_p; } -template<> -void invoke_callback(Command* cmd_obj, redisReply* reply) { - if(reply->type != REDIS_REPLY_STRING && reply->type != REDIS_REPLY_STATUS) { - cerr << "[ERROR] " << cmd_obj->cmd << ": Received non-string reply." << endl; - cmd_obj->invoke_error(REDISX_WRONG_TYPE); - return; - } - cmd_obj->invoke(reply->str); -} - template<> unordered_map*>& Redox::get_command_map() { return commands_int; } -template<> -void invoke_callback(Command* cmd_obj, redisReply* reply) { - if(reply->type != REDIS_REPLY_INTEGER) { - cerr << "[ERROR] " << cmd_obj->cmd << ": Received non-integer reply." << endl; - cmd_obj->invoke_error(REDISX_WRONG_TYPE); - return; - } - cmd_obj->invoke((int)reply->integer); -} - template<> unordered_map*>& Redox::get_command_map() { return commands_long_long_int; } -template<> -void invoke_callback(Command* cmd_obj, redisReply* reply) { - if(reply->type != REDIS_REPLY_INTEGER) { - cerr << "[ERROR] " << cmd_obj->cmd << ": Received non-integer reply." << endl; - cmd_obj->invoke_error(REDISX_WRONG_TYPE); - return; - } - cmd_obj->invoke(reply->integer); -} +template<> unordered_map*>& +Redox::get_command_map() { return commands_null; } // ---------------------------- // Helpers @@ -277,7 +206,7 @@ void Redox::command(const string& cmd) { bool Redox::command_blocking(const string& cmd) { Command* c = command_blocking(cmd); - bool succeeded = (c->status() == REDISX_OK); + bool succeeded = (c->status() == REDOX_OK); c->free(); return succeeded; } diff --git a/src/redox.hpp b/src/redox.hpp index 879dcf0..03c00bc 100644 --- a/src/redox.hpp +++ b/src/redox.hpp @@ -89,6 +89,7 @@ private: std::unordered_map*> commands_char_p; std::unordered_map*> commands_int; std::unordered_map*> commands_long_long_int; + std::unordered_map*> commands_null; template std::unordered_map*>& get_command_map(); @@ -142,7 +143,7 @@ template Command* Redox::command_blocking(const std::string& cmd) { ReplyT val; - std::atomic_int status(REDISX_UNINIT); + std::atomic_int status(REDOX_UNINIT); std::condition_variable cv; std::mutex m; @@ -153,7 +154,7 @@ Command* Redox::command_blocking(const std::string& cmd) { [&val, &status, &m, &cv](const std::string& cmd_str, const ReplyT& reply) { std::unique_lock ul(m); val = reply; - status = REDISX_OK; + status = REDOX_OK; ul.unlock(); cv.notify_one(); }, @@ -167,7 +168,7 @@ Command* Redox::command_blocking(const std::string& cmd) { ); // Wait until a callback is invoked - cv.wait(lk, [&status] { return status != REDISX_UNINIT; }); + cv.wait(lk, [&status] { return status != REDOX_UNINIT; }); cmd_obj->reply_val = val; cmd_obj->reply_status = status;