/** * Redis C++11 wrapper. */ #include #include #include #include "command.hpp" #include "redox.hpp" using namespace std; namespace redox { template Command::Command( Redox* rdx, long id, const std::string& cmd, const std::function&)>& callback, double repeat, double after, bool free_memory, log::Logger& logger ) : rdx_(rdx), id_(id), cmd_(cmd), repeat_(repeat), after_(after), free_memory_(free_memory), callback_(callback), logger_(logger) { timer_guard_.lock(); } template void Command::processReply(redisReply* r) { free_guard_.lock(); reply_obj_ = r; parseReplyObject(); invoke(); pending_--; // Allow free() method to free memory if (!free_memory_) { // logger.trace() << "Command memory not being freed, free_memory = " << free_memory; free_guard_.unlock(); return; } freeReply(); // Handle memory if all pending replies have arrived if (pending_ == 0) { // Just free non-repeating commands if (repeat_ == 0) { freeCommand(this); return; // Free repeating commands if timer is stopped } else { if ((long)(timer_.data) == 0) { freeCommand(this); return; } } } free_guard_.unlock(); } template void Command::free() { free_guard_.lock(); freeReply(); free_guard_.unlock(); freeCommand(this); } template void Command::freeReply() { if (reply_obj_ == nullptr) { logger_.error() << cmd_ << ": Attempting to double free reply object."; return; } freeReplyObject(reply_obj_); reply_obj_ = nullptr; } template void Command::freeCommand(Command* c) { c->rdx_->template remove_active_command(c->id_); // logger.debug() << "Deleted Command " << c->id << " at " << c; delete c; } template const ReplyT& Command::reply() const { if (!ok()) { logger_.warning() << cmd_ << ": Accessing value of reply with status != OK."; } return reply_val_; } template bool Command::isExpectedReply(int type) { if(reply_obj_->type == type) { reply_status_ = OK_REPLY; return true; } if(checkErrorReply() || checkNilReply()) return false; logger_.error() << cmd_ << ": Received reply of type " << reply_obj_->type << ", expected type " << type << "."; reply_status_ = WRONG_TYPE; return false; } template bool Command::isExpectedReply(int typeA, int typeB) { if((reply_obj_->type == typeA) || (reply_obj_->type == typeB)) { reply_status_ = OK_REPLY; return true; } if(checkErrorReply() || checkNilReply()) return false; logger_.error() << cmd_ << ": Received reply of type " << reply_obj_->type << ", expected type " << typeA << " or " << typeB << "."; reply_status_ = WRONG_TYPE; return false; } template bool Command::checkErrorReply() { if (reply_obj_->type == REDIS_REPLY_ERROR) { logger_.error() << cmd_ << ": " << reply_obj_->str; reply_status_ = ERROR_REPLY; return true; } return false; } template bool Command::checkNilReply() { if (reply_obj_->type == REDIS_REPLY_NIL) { logger_.warning() << cmd_ << ": Nil reply."; reply_status_ = NIL_REPLY; return true; } return false; } // ---------------------------------------------------------------------------- // Specializations of parseReplyObject for all expected return types // ---------------------------------------------------------------------------- template<> void Command::parseReplyObject() { reply_val_ = reply_obj_; } template<> void Command::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_STRING, REDIS_REPLY_STATUS)) return; reply_val_ = {reply_obj_->str, static_cast(reply_obj_->len)}; } template<> void Command::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_STRING, REDIS_REPLY_STATUS)) return; reply_val_ = reply_obj_->str; } template<> void Command::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_INTEGER)) return; reply_val_ = (int) reply_obj_->integer; } template<> void Command::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_INTEGER)) return; reply_val_ = reply_obj_->integer; } template<> void Command::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_NIL)) return; reply_val_ = nullptr; } template<> void Command>::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_ARRAY)) return; for(size_t i = 0; i < reply_obj_->elements; i++) { redisReply* r = *(reply_obj_->element + i); reply_val_.emplace_back(r->str, r->len); } } template<> void Command>::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_ARRAY)) return; for(size_t i = 0; i < reply_obj_->elements; i++) { redisReply* r = *(reply_obj_->element + i); reply_val_.emplace(r->str, r->len); } } template<> void Command>::parseReplyObject() { if(!isExpectedReply(REDIS_REPLY_ARRAY)) return; for(size_t i = 0; i < reply_obj_->elements; i++) { redisReply* r = *(reply_obj_->element + i); reply_val_.emplace(r->str, r->len); } } // Explicit template instantiation for available types, so that the generated // library contains them and we can keep the method definitions out of the // header file. template class Command; template class Command; template class Command; template class Command; template class Command; template class Command; template class Command>; template class Command>; template class Command>; } // End namespace redox