redis-plus-plus

C++Cache LibraryRedisClientNoSQL

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

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;
    }
}