Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

simplified command execution - the Connection is an implementation de… #2

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ else()
cmake_minimum_required(VERSION 2.8.0)
endif()

set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -W -Werror -fPIC")
set(CMAKE_CXX_FLAGS "-std=c++14 -Wall -Wno-narrowing -Wno-sign-compare -W -Werror -fPIC")

set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

Expand Down
58 changes: 58 additions & 0 deletions src/sw/redis++/is_command_functor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**************************************************************************
Copyright (c) 2018 sewenew

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/

#ifndef IS_COMMAND_FUNCTOR_H
#define IS_COMMAND_FUNCTOR_H

#include <type_traits>
#include "connection.h"

namespace sw {

namespace redis {
namespace detail{
/**
* return type for good candidates
*/
template<class...> struct CanBeCalled : std::true_type{
};

/**
* true_type, if Callable can be called with the given parameter pack expanded.
*/
template<class Callable, class ... Args>
auto testCallConnection(int)
->CanBeCalled<decltype(std::declval<Callable>()(std::declval<Connection&>(),
std::declval<typename std::add_rvalue_reference<Args>::type>()...))>;

template<class NotCallable, class ...>
auto testCallConnection(long)->std::false_type;
}

template<class T, class ... TrailingArgs>
struct CanBeCalledWithConnectionAndTrailingArgs : decltype(detail::testCallConnection<T, TrailingArgs ...>(0)){
};

/**
* A command functor is something that can be called with a Connection& as first parameter and a given list of trailing arguments.
* Compile time flag to check whether an instance of the given type T can be used as command functor.
*/
template<class T, class ... TrailingArgs>
constexpr bool isCommandFunctor_v = CanBeCalledWithConnectionAndTrailingArgs<T, TrailingArgs...>::value;
}
}

#endif
13 changes: 12 additions & 1 deletion src/sw/redis++/redis.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "subscriber.h"
#include "pipeline.h"
#include "transaction.h"
#include "is_command_functor.h"

namespace sw {

Expand Down Expand Up @@ -62,7 +63,17 @@ class Redis {
Subscriber subscriber();

template <typename Cmd, typename ...Args>
ReplyUPtr command(Cmd cmd, Args &&...args);
auto command(Cmd cmd, Args &&...args)->std::enable_if_t<isCommandFunctor_v<Cmd, Args...>, ReplyUPtr>;

/**
* simplified command execution. cmd is a redis command string with format specifiers, matching the trailing args.
* Usage example:
* Redis redis("tcp://127.0.0.1:6379");
* std::string clientName("Hugo");
* redis.command("CLIENT SETNAME %s",clientName.c_str());
*/
template <typename ...Args>
ReplyUPtr command(const StringView & cmd, Args &&...args);

// CONNECTION commands.

Expand Down
9 changes: 8 additions & 1 deletion src/sw/redis++/redis.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace sw {
namespace redis {

template <typename Cmd, typename ...Args>
ReplyUPtr Redis::command(Cmd cmd, Args &&...args) {
auto Redis::command(Cmd cmd, Args &&...args)->std::enable_if_t<isCommandFunctor_v<Cmd, Args...>, ReplyUPtr> {
if (_connection) {
// Single Connection Mode.
if (_connection->broken()) {
Expand All @@ -47,6 +47,13 @@ ReplyUPtr Redis::command(Cmd cmd, Args &&...args) {
}
}

template <typename ...Args>
ReplyUPtr Redis::command(const StringView & cmdString, Args &&...args) {
auto cmd = [&cmdString](Connection &connection, Args &&...args){
connection.send(cmdString.data(), std::forward<Args>(args)...);};
return Redis::command(cmd, std::forward<Args>(args)...);
}

// KEY commands.

template <typename Input>
Expand Down
68 changes: 68 additions & 0 deletions test/src/sw/redis++/IsCommandFunctorTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**************************************************************************
Copyright (c) 2018 sewenew

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#include <sw/redis++/is_command_functor.h>
#include <vector>

using namespace sw::redis;
namespace {
void connectionFunction(Connection&){}

struct IsCommandFunctorTest {
static void testStringNoTrailingArgs() {
static_assert(!isCommandFunctor_v<std::string>, "a string is not a command functor");
}

static void testStringIntTrailingArg() {
static_assert(!isCommandFunctor_v<std::string, int>, "a string is not a command functor");
}

static void testIsConnectionFunction(){
static_assert(isCommandFunctor_v<decltype(connectionFunction)>, "connectionFunction is a functor");
}

static void testLambdaNoTrailingArgs() {
StringView cmdString = "CLIENT GETNAME";
auto lambdaCmd = [&cmdString](Connection &){};
static_assert(isCommandFunctor_v<decltype(lambdaCmd)>,"no trailing arg lambda is a functor");
}

static void testLambdaWithTrailingArgs() {
StringView cmdString = "CLIENT SETNAME %s";
auto lambdaCmd = [&cmdString](Connection &, char const*){};
static_assert(isCommandFunctor_v<decltype(lambdaCmd),char const*>,"trailing arg lambda is a functor");
}

template<class... TrailingArgs>
static void testLambdaWithTrailingArgs(TrailingArgs&&...) {
auto lambda = [](Connection&,TrailingArgs&&...){};
static_assert(isCommandFunctor_v<decltype(lambda),TrailingArgs...>,"trailing arg lambda is a functor");
}

/**
* test that rvalue references are supported. Note that currently the Redis::command(StringView const&, ...)
* needs to support rvalue references
*/
static void testLambdaWithTrailingRValueArgs(){
testLambdaWithTrailingArgs(int());
}

static void testLambdaWithTrailingLValueArg() {
std::vector<int> lvalue;
auto lambda = [](Connection &, std::vector<int>& v){v.push_back(23);};
static_assert(isCommandFunctor_v<decltype(lambda),std::vector<int>&>,"trailing arg lambda is a functor");
}
};
}