redis-plus-plus
Cache Library
redis-plus-plus
Overview
redis-plus-plus is a modern Redis client library for C++11 and later, providing high-performance implementation based on hiredis with STL-like interfaces, making Redis operations in C++ applications simple and efficient.
Details
redis-plus-plus is a C++ Redis client library built on top of the hiredis library, supporting C++11 and later standards. It works not only with Redis but also with other key-value stores that support RESP (Redis Serialization Protocol) like Valkey, DragonflyDB, and KeyDB. Key features include most Redis commands, connection pool, Redis scripting, thread safety (unless otherwise stated), Redis Pub/Sub, pipelining, transactions, Redis Cluster, Redis Sentinel, STL-like interfaces, generic command interface, Redis Stream, TLS/SSL support, sync and async interfaces, and coroutine support. It's described as "production ready and fully tested with many compilers" and provides a generic command interface that can send any command to Redis 5.0 and corresponding clusters. The library is also available through package managers like Conan, adapting well to modern C++ development environments.
Pros and Cons
Pros
- Modern C++ Support: Designed with C++11 and later standard features
- High Performance: Fast Redis communication based on hiredis
- Rich Features: Full support for Cluster, Sentinel, Stream, TLS, etc.
- STL-like Interface: Familiar interface for C++ developers
- Thread Safe: Safe usage in multi-threaded environments
- Versatility: Works with Redis and other RESP-compatible stores
- Async Support: Support for sync, async, and coroutine patterns
Cons
- C++ Complexity: C++-specific complexity and long compilation times
- Dependencies: Dependency on hiredis library and complex build configuration
- Learning Curve: Requires deep understanding of both Redis and C++
- Debugging Challenges: Complex debugging due to C++'s low-level nature
- Platform Constraints: C++ compiler and platform-specific limitations
Key Links
- redis-plus-plus GitHub Repository
- redis-plus-plus Conan Package
- hiredis GitHub Repository
- Redis Official Client Libraries
- Redis Official Site
Example Usage
Basic Connection and Setup
#include <sw/redis++/redis++.h>
using namespace sw::redis;
try {
// Basic connection
Redis redis("tcp://127.0.0.1:6379");
// Connection with configuration
ConnectionOptions connection_options;
connection_options.host = "127.0.0.1";
connection_options.port = 6379;
connection_options.password = "auth";
connection_options.db = 1;
ConnectionPoolOptions pool_options;
pool_options.size = 3;
Redis redis_with_pool(connection_options, pool_options);
} catch (const Error &e) {
std::cerr << "Connection error: " << e.what() << std::endl;
}
Basic Key-Value Operations
#include <iostream>
#include <sw/redis++/redis++.h>
int main() {
try {
sw::redis::Redis redis("tcp://127.0.0.1:6379");
// Set and get strings
redis.set("key", "value");
auto val = redis.get("key");
if (val) {
std::cout << "Value: " << *val << std::endl;
}
// Numeric operations
redis.set("counter", "0");
auto count = redis.incr("counter");
std::cout << "Counter: " << count << std::endl;
// Key with expiration
redis.setex("temp_key", std::chrono::seconds(60), "temp_value");
// Check key existence
if (redis.exists("key")) {
std::cout << "Key exists" << std::endl;
}
// Delete key
redis.del("key");
} catch (const sw::redis::Error &e) {
std::cerr << "Redis error: " << e.what() << std::endl;
}
return 0;
}
List and Set Operations
// List operations
redis.lpush("list", {"a", "b", "c"});
redis.rpush("list", {"d", "e"});
std::vector<std::string> list_items;
redis.lrange("list", 0, -1, std::back_inserter(list_items));
for (const auto& item : list_items) {
std::cout << item << " ";
}
// Set operations
redis.sadd("set", {"member1", "member2", "member3"});
std::unordered_set<std::string> set_members;
redis.smembers("set", std::inserter(set_members, set_members.begin()));
for (const auto& member : set_members) {
std::cout << member << " ";
}
// Set operations
redis.sadd("set1", {"a", "b", "c"});
redis.sadd("set2", {"b", "c", "d"});
std::unordered_set<std::string> intersection;
redis.sinter({"set1", "set2"}, std::inserter(intersection, intersection.begin()));
Hash Operations
// Set hash fields
redis.hset("user:1", "name", "John");
redis.hset("user:1", "email", "[email protected]");
redis.hset("user:1", "age", "30");
// Bulk set multiple fields
std::unordered_map<std::string, std::string> user_data = {
{"name", "Alice"},
{"email", "[email protected]"},
{"age", "25"}
};
redis.hmset("user:2", user_data.begin(), user_data.end());
// Get hash field
auto name = redis.hget("user:1", "name");
if (name) {
std::cout << "Name: " << *name << std::endl;
}
// Get all fields
std::unordered_map<std::string, std::string> all_fields;
redis.hgetall("user:1", std::inserter(all_fields, all_fields.begin()));
for (const auto& [field, value] : all_fields) {
std::cout << field << ": " << value << std::endl;
}
Pipelining
// Execute multiple commands efficiently with pipeline
auto pipe = redis.pipeline();
auto future1 = pipe.set("key1", "value1");
auto future2 = pipe.set("key2", "value2");
auto future3 = pipe.get("key1");
auto future4 = pipe.get("key2");
pipe.exec();
// Get results
try {
future1.get(); // set result
future2.get(); // set result
auto val1 = future3.get(); // get result
auto val2 = future4.get(); // get result
if (val1) std::cout << "Key1: " << *val1 << std::endl;
if (val2) std::cout << "Key2: " << *val2 << std::endl;
} catch (const sw::redis::Error &e) {
std::cerr << "Pipeline error: " << e.what() << std::endl;
}
Transactions
// Transaction processing
auto tx = redis.transaction();
tx.multi();
tx.set("account:1:balance", "100");
tx.set("account:2:balance", "200");
tx.decrby("account:1:balance", 50);
tx.incrby("account:2:balance", 50);
auto results = tx.exec();
if (results) {
std::cout << "Transaction successful" << std::endl;
} else {
std::cout << "Transaction failed" << std::endl;
}
Redis Cluster Operations
#include <sw/redis++/redis++.h>
// Redis Cluster connection
ConnectionOptions connection_options;
connection_options.host = "127.0.0.1";
connection_options.port = 7000;
ConnectionPoolOptions pool_options;
pool_options.size = 3;
RedisCluster cluster(connection_options, pool_options);
// Cluster operations
cluster.set("key", "value");
auto val = cluster.get("key");
// Get cluster information
auto nodes = cluster.cluster_nodes();
std::cout << "Cluster nodes: " << nodes << std::endl;
Pub/Sub Feature
// Publisher
redis.publish("channel1", "Hello, World!");
// Subscriber
auto sub = redis.subscriber();
sub.subscribe("channel1");
sub.consume();
while (true) {
try {
auto msg = sub.consume();
if (msg.type == sw::redis::Subscriber::MsgType::MESSAGE) {
std::cout << "Received: " << msg.msg << std::endl;
}
} catch (const sw::redis::TimeoutError &e) {
// Timeout handling
continue;
} catch (const sw::redis::Error &e) {
std::cerr << "Subscriber error: " << e.what() << std::endl;
break;
}
}
Asynchronous Operations
#include <future>
#include <sw/redis++/redis++.h>
// Asynchronous Redis operations
auto async_redis = std::async(std::launch::async, [&redis]() {
try {
redis.set("async_key", "async_value");
auto val = redis.get("async_key");
return val ? *val : std::string("Not found");
} catch (const sw::redis::Error &e) {
return std::string("Error: ") + e.what();
}
});
// Execute other work
std::cout << "Doing other work..." << std::endl;
// Get async result
auto result = async_redis.get();
std::cout << "Async result: " << result << std::endl;
Error Handling
#include <sw/redis++/redis++.h>
void robust_redis_operation() {
try {
Redis redis("tcp://127.0.0.1:6379");
// Execute operations
redis.set("test_key", "test_value");
auto val = redis.get("test_key");
} catch (const ConnectionError &e) {
std::cerr << "Connection error: " << e.what() << std::endl;
// Reconnection logic
} catch (const TimeoutError &e) {
std::cerr << "Timeout error: " << e.what() << std::endl;
// Timeout handling
} catch (const ReplyError &e) {
std::cerr << "Redis reply error: " << e.what() << std::endl;
// Redis-specific error handling
} catch (const Error &e) {
std::cerr << "Generic Redis error: " << e.what() << std::endl;
// Generic error handling
} catch (const std::exception &e) {
std::cerr << "Standard exception: " << e.what() << std::endl;
}
}