Skip to content

Repository files navigation

redis-cpp - lightweight C++ client library for Redis

redis-cpp is a C++17 library for executing Redis commands with support for pipelines and the publish / subscribe pattern. Moreover, you can extend the library with your own stream implementation to communicate with Redis. You can also use it like a RESP serializer (pure core). You need only know a couple of functions to start working with Redis.

// Connect to serverauto stream = rediscpp::make_stream("localhost", "6379");
// Execute command
std::cout << rediscpp::execute(*stream, "ping").as<std::string>() << std::endl;

And you may dive deeper if you feel the need.

NOTE
If you need a C++11 version you could switch to c++11 branch and use that one.

Version

1.1.0

Features

  • easy way to access Redis
  • pipelines
  • publish / subscribe
  • pure core in C++ for the RESP
  • extensible transport
  • header-only library if it's necessary
  • minimal dependencies
  • various levels of usage

License

Distributed under the MIT License

Compiler and OS

This has compiled and tested within gcc 9.3 and clang 10.0 on Ubuntu 20.04.
You might try other compiler or OS.

NOTE
All code is a cross-platform.

Dependencies

  • Boost (at least 1.71 only for using with built-in implementation of transport).

Build and install

Build library

git clone https://github.com/tdv/redis-cpp.git cd redis-cpp
mkdir build cd build cmake .. make make install 

You can use CMAKE_INSTALL_PREFIX to select the installation directory
Moreover, you can use cmake options to configure the library for header-only or pure core.
Instead of cmake options, you can define REDISCPP_HEADER_ONLY and use the library as header-only without any cmake file.

NOTE
redis-cpp has two build options

  • Pure core only
  • Header-only

Use cmake -D with REDISCPP_HEADER_ONLY or REDISCPP_PURE_CORE. You can enable both options at the same time.
You can use your own transport with the 'pure core' option.

If you need to use the header-only library, you can copy the folder redis-cpp from include/redis-cpp in your project and define the macro REDISCPP_HEADER_ONLY before including the redis-cpp headers following the example code below:

#defineREDISCPP_HEADER_ONLY
#include<redis-cpp/stream.h>
#include<redis-cpp/execute.h>// Include something else

Build examples

cd examples/{example_project}
mkdir build cd build cmake .. make 

Examples

NOTE
Look at the redis-docker folder to get all you need to start testing redis-cpp. There are files to build and run a Redis server in Docker.

Ping

Source code
Description
The "Ping" example demonstrates how to execute a Redis command.

// STD
#include<cstdlib>
#include<iostream>
#include<redis-cpp/stream.h>
#include<redis-cpp/execute.h>intmain()
{
try
{
auto stream = rediscpp::make_stream("localhost", "6379");
auto response = rediscpp::execute(*stream, "ping");
std::cout << response.as<std::string>() << std::endl;
}
catch (std::exception const &e)
{
std::cerr << "Error: " << e.what() << std::endl;
returnEXIT_FAILURE;
}
returnEXIT_SUCCESS;
}

Set and Get data

Source code
Description
The example demonstrates how to set and get a value.

// STD
#include<cstdlib>
#include<iostream>
#include<redis-cpp/stream.h>
#include<redis-cpp/execute.h>intmain()
{
try
{
auto stream = rediscpp::make_stream("localhost", "6379");
autoconst key = "my_key";
auto response = rediscpp::execute(*stream, "set",
key, "Some value for 'my_key'", "ex", "60");
std::cout << "Set key '" << key << "': " << response.as<std::string>() << std::endl;
response = rediscpp::execute(*stream, "get", key);
std::cout << "Get key '" << key << "': " << response.as<std::string>() << std::endl;
}
catch (std::exception const &e)
{
std::cerr << "Error: " << e.what() << std::endl;
returnEXIT_FAILURE;
}
returnEXIT_SUCCESS;
}

Pipeline

Source code
Description
It's a more complicated example which demonstrates how to use a pipeline within Redis to achieve better performance.

// STD
#include<cstdlib>
#include<iostream>
#include<redis-cpp/stream.h>
#include<redis-cpp/execute.h>intmain()
{
try
{
auto stream = rediscpp::make_stream("localhost", "6379");
intconst N = 10;
autoconst key_pref = "my_key_";
// Executing command 'SET' N times without getting any responsefor (int i = 0 ; i < N ; ++i)
{
autoconst item = std::to_string(i);
rediscpp::execute_no_flush(*stream,
"set", key_pref + item, item , "ex", "60");
}
// Flush allstd::flush(*stream);
// Getting response for each sent 'SET' requestfor (int i = 0 ; i < N ; ++i)
{
rediscpp::value value{*stream};
std::cout << "Set " << key_pref << i << ": "
<< value.as<std::string_view>() << std::endl;
}
// Executing command 'GET' N times without getting any responsefor (int i = 0 ; i < N ; ++i)
{
rediscpp::execute_no_flush(*stream, "get",
key_pref + std::to_string(i));
}
// Flush allstd::flush(*stream);
// Getting response for each sent 'GET' requestfor (int i = 0 ; i < N ; ++i)
{
rediscpp::value value{*stream};
std::cout << "Get " << key_pref << i << ": "
<< value.as<std::string_view>() << std::endl;
}
}
catch (std::exception const &e)
{
std::cerr << "Error: " << e.what() << std::endl;
returnEXIT_FAILURE;
}
returnEXIT_SUCCESS;
}

Resp

Source code
Description
The "Resp" example demonstrates a basic RESP serialization within redis-cpp without communication with Redis server. It's meant to show you how to use RESP serialization with redis-cpp library.

// STD
#include<cstdlib>
#include<iostream>
#include<sstream>
#include<redis-cpp/execute.h>namespaceresps= rediscpp::resp::serialization;
namespacerespds= rediscpp::resp::deserialization;
automake_sample_data()
{
std::ostringstream stream;
put(stream, resps::array{
resps::simple_string{"This is a simple string."},
resps::error_message{"This is an error message."},
resps::bulk_string{"This is a bulk string."},
resps::integer{100500},
resps::array{
resps::simple_string("This is a simple string in a nested array."),
resps::bulk_string("This is a bulk string in a nested array.")
}
});
return stream.str();
}
voidprint_value(respds::array::item_type const &value, std::ostream &stream)
{
std::visit(rediscpp::resp::detail::overloaded{
[&stream] (respds::simple_string const &val)
{ stream << "Simple string: " << val.get() << std::endl; },
[&stream] (respds::error_message const &val)
{ stream << "Error message: " << val.get() << std::endl; },
[&stream] (respds::bulk_string const &val)
{ stream << "Bulk string: " << val.get() << std::endl; },
[&stream] (respds::integer const &val)
{ stream << "Integer: " << val.get() << std::endl; },
[&stream] (respds::array const &val)
{
stream << "----- Array -----" << std::endl;
for (autoconst &i : val.get())
print_value(i, stream);
stream << "-----------------" << std::endl;
},
[&stream] (autoconst &)
{ stream << "Unexpected value type." << std::endl; }
}, value);
}
voidprint_sample_data(std::istream &istream, std::ostream &ostream)
{
rediscpp::value value{istream};
print_value(value.get(), ostream);
}
intmain()
{
try
{
autoconst data = make_sample_data();
std::cout << "------------ Serialization ------------" << std::endl;
std::cout << data << std::endl;
std::cout << "------------ Deserialization ------------" << std::endl;
std::istringstream stream{data};
print_sample_data(stream, std::cout);
std::cout << std::endl;
}
catch (std::exception const &e)
{
std::cerr << "Error: " << e.what() << std::endl;
returnEXIT_FAILURE;
}
returnEXIT_SUCCESS;
}

Publish / Subscribe

Source code
Description
This is a more complicated example within redis-cpp which demonstrates how to publish messages and create a subscription to a queue. In the example a publisher and subscriber located in one process simultaniously, each one has its own stream to communicate with Redis. Usually, in real projects the publisher and subscriber are not located in one process.

// STD
#include<cstdlib>
#include<iostream>
#include<thread>// BOOST
#include<boost/thread.hpp>
#include<redis-cpp/stream.h>
#include<redis-cpp/execute.h>intmain()
{
try
{
autoconst N = 100;
autoconst queue_name = "test_queue";
boolvolatile stopped = false;
// A message printer. The message from a queue.auto print_message = [] (autoconst &value)
{
usingnamespacerediscpp::resp::deserialization;std::visit(rediscpp::resp::detail::overloaded{
[] (bulk_string const &val)
{ std::cout << val.get() << std::endl; },
[] (autoconst &)
{ std::cout << "Unexpected value type." << std::endl; }
}, value);
};
// The subscriber is run in its own thread.// It's some artificial example, when publisher// and subscriber are working in one process.// It's only for demonstration library abilities.
boost::thread subscriber{
[&stopped, &queue_name, &print_message]
{
// Its own stream for a subscriberauto stream = rediscpp::make_stream("localhost", "6379");
auto response = rediscpp::execute(*stream, "subscribe", queue_name);
// An almost endless loop for getting messages from the queues.while (!stopped)
{
// Reading / waiting for a message.
rediscpp::value value{*stream};
// Message extraction.std::visit(rediscpp::resp::detail::overloaded{
// We're wondered only an array in response.// Otherwise, there is an error.
[&print_message] (rediscpp::resp::deserialization::array const &arr)
{
std::cout << "-------- Message --------" << std::endl;
for (autoconst &i : arr.get())
print_message(i);
std::cout << "-------------------------" << std::endl;
},
// Oops. An error in a response.
[] (rediscpp::resp::deserialization::error_message const &err)
{ std::cerr << "Error: " << err.get() << std::endl; },
// An unexpected response.
[] (autoconst &)
{ std::cout << "Unexpected value type." << std::endl; }
}, value.get());
}
}
};
// An artificial delay. It's not necessary in real code.std::this_thread::sleep_for(std::chrono::milliseconds{200});
// Its own stream for a publisher.auto stream = rediscpp::make_stream("localhost", "6379");
// The publishing N messages.for (int i = 0 ; i < N ; ++i)
{
auto response = rediscpp::execute(*stream,
"publish", queue_name, std::to_string(i));
std::cout << "Delivered to " << response.as<std::int64_t>()
<< " subscribers." << std::endl;
}
// An artificial delay. It's not necessary in real code.// It's due to the artificiality of the example,// where everything is in one process.std::this_thread::sleep_for(std::chrono::milliseconds{200});
stopped = true;
std::this_thread::sleep_for(std::chrono::milliseconds{200});
// Why not?... Please, avoid it in real code.// It's justified only in examples.
subscriber.interrupt();
}
catch (std::exception const &e)
{
std::cerr << "Error: " << e.what() << std::endl;
returnEXIT_FAILURE;
}
returnEXIT_SUCCESS;
}

Conclusion

Take a look at a code above one more time. I hope you can find something useful for your own projects with Redis. I'd thought about adding one more level to wrap all Redis commands and refused this idea. A lot of useless work with a small outcome, because, in many cases we need to run only a handful of commands. Maybe it'll be a good idea in the future. Now you can use redis-cpp like lightweight library to execute Redis commands and get results with minimal effort.

Enjoy your own projects with Redis!

About

redis-cpp is a header-only library in C++17 for Redis (and C++11 backport)

Topics

Resources

Stars

136 stars

Watchers

10 watching

Forks

Releases

Packages

Used by

Contributors

Languages