CPR (C++ Requests)

Python RequestsライブラリからインスパイアされたC++HTTPクライアント。libcurlのラッパーとしてモダンC++17のAPIを提供。直感的な構文でHTTPリクエストを構築でき、JSON処理、認証、SSL設定、プロキシサポート等の機能を含む。

HTTPクライアントC++libcurlシンプル認証非同期

GitHub概要

libcpr/cpr

C++ Requests: Curl for People, a spiritual port of Python Requests.

スター7,160
ウォッチ130
フォーク1,012
作成日:2015年3月31日
言語:C++
ライセンス:Other

トピックス

c-plus-pluscpphacktoberfesthttplibcurllibraryrequests

スター履歴

libcpr/cpr Star History
データ取得日時: 2025/10/22 09:55

ライブラリ

cpr

概要

cprは「C++ Requests: Curl for People」として開発された、PythonのRequestsライブラリにインスパイアされたモダンなC++用HTTPクライアントライブラリです。libcurlの強力な機能を簡潔で直感的なAPIで提供し、複雑なHTTP通信をC++で効率的に実装可能。同期・非同期リクエスト、包括的認証システム、SSL/TLS完全サポート、マルチパートアップロード、プロキシ設定など、企業レベルのHTTP通信要件を満たす豊富な機能を提供し、C++における事実上の標準HTTPライブラリとして地位を確立しています。

詳細

cpr 2025年版はC++17をベースとしたモダンなHTTPクライアントライブラリとして進化を続けています。libcurlの複雑なAPIを抽象化しつつ、その強力な機能へのフルアクセスを維持する設計により、C++開発者にとって使いやすいHTTP通信環境を提供。セッションベースAPI、利便性関数、マルチリクエスト処理、インターセプター機能など、多様な開発パターンに対応。CMakeFetchContentによる簡単な統合、クロスプラットフォーム対応、継続的な開発により、パフォーマンスと保守性を兼ね備えたHTTP通信ソリューションとして企業開発で広く採用されています。

主な特徴

  • PythonライクなAPI: Requestsライブラリに触発された直感的で読みやすいインターフェース
  • libcurlラッパー: 強力なlibcurl機能をシンプルなC++APIで利用可能
  • 同期・非同期対応: ブロッキングとノンブロッキング両方のリクエストパターン
  • 包括的認証サポート: Basic、Digest、Bearer Token、NTLM認証対応
  • セッション管理: 効率的な接続プールと設定の再利用機能
  • マルチリクエスト: 複数のHTTPリクエストの並列実行サポート

メリット・デメリット

メリット

  • C++開発者にとって直感的で学習コストの低いAPIデザイン
  • libcurlの複雑さを隠蔽しつつ、全機能へのアクセスを維持
  • 同期・非同期・マルチリクエストによる柔軟な実装パターン
  • CMake FetchContentによる簡単なプロジェクト統合
  • 豊富な認証方式とSSL/TLS設定による企業レベル対応
  • クロスプラットフォーム対応(Linux、Windows、macOS)

デメリット

  • libcurlへの依存によるビルド設定の複雑さ
  • C++17以降の要件により古いコンパイラでは利用不可
  • PythonのRequestsと比較してビルドシステムの理解が必要
  • 大量の並列リクエストでのメモリ使用量増加
  • libcurlのバージョン要件(>= 7.64.0)による環境制約
  • C++の特性上、エラーハンドリングがPythonより煩雑

参考ページ

書き方の例

インストールと基本セットアップ

# CMakeLists.txt でのFetchContent使用(推奨)
include(FetchContent)
FetchContent_Declare(cpr 
    GIT_REPOSITORY https://github.com/libcpr/cpr.git
    GIT_TAG 1.10.5)  # 最新バージョンを指定
FetchContent_MakeAvailable(cpr)

# ターゲットにリンク
target_link_libraries(your_target_name PRIVATE cpr::cpr)
# システムにインストール(find_packageで使用)
git clone https://github.com/libcpr/cpr.git
cd cpr && mkdir build && cd build
cmake .. -DCPR_USE_SYSTEM_CURL=ON
cmake --build . --parallel
sudo cmake --install .
// 基本的なインクルード
#include <cpr/cpr.h>
#include <iostream>

// バージョン確認
#include <cpr/cpr.h>
std::cout << "cpr version: " << CPR_VERSION << std::endl;

基本的なリクエスト(GET/POST/PUT/DELETE)

#include <cpr/cpr.h>

// 基本的なGETリクエスト
cpr::Response r = cpr::Get(cpr::Url{"https://api.example.com/users"});
std::cout << r.status_code << std::endl;  // 200
std::cout << r.header["content-type"] << std::endl;  // application/json
std::cout << r.text << std::endl;  // レスポンスボディ

// パラメータ付きGETリクエスト
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/users"},
    cpr::Parameters{{"page", "1"}, {"limit", "10"}, {"sort", "created_at"}}
);
std::cout << r.url << std::endl;  // 最終的なURL(パラメータ付き)

// POSTリクエスト(JSONデータ送信)
cpr::Response r = cpr::Post(
    cpr::Url{"https://api.example.com/users"},
    cpr::Body{R"({"name": "田中太郎", "email": "[email protected]", "age": 30})"},
    cpr::Header{{"Content-Type", "application/json"}, 
               {"Authorization", "Bearer your-token"}}
);

if (r.status_code == 201) {
    std::cout << "ユーザー作成成功: " << r.text << std::endl;
} else {
    std::cout << "エラー: " << r.status_code << " - " << r.text << std::endl;
}

// POSTリクエスト(フォームデータ送信)
cpr::Response r = cpr::Post(
    cpr::Url{"https://api.example.com/login"},
    cpr::Payload{{"username", "testuser"}, {"password", "secret123"}}
);

// PUTリクエスト(データ更新)
cpr::Response r = cpr::Put(
    cpr::Url{"https://api.example.com/users/123"},
    cpr::Body{R"({"name": "田中次郎", "email": "[email protected]"})"},
    cpr::Header{{"Content-Type", "application/json"},
               {"Authorization", "Bearer your-token"}}
);

// DELETEリクエスト
cpr::Response r = cpr::Delete(
    cpr::Url{"https://api.example.com/users/123"},
    cpr::Header{{"Authorization", "Bearer your-token"}}
);

if (r.status_code == 204) {
    std::cout << "ユーザー削除完了" << std::endl;
}

// レスポンス属性の詳細確認
std::cout << "ステータスコード: " << r.status_code << std::endl;
std::cout << "理由: " << r.reason << std::endl;
std::cout << "最終URL: " << r.url << std::endl;
std::cout << "経過時間: " << r.elapsed << " 秒" << std::endl;
std::cout << "アップロード済み: " << r.uploaded_bytes << " バイト" << std::endl;
std::cout << "ダウンロード済み: " << r.downloaded_bytes << " バイト" << std::endl;

認証とヘッダー設定(Advanced Configuration)

#include <cpr/cpr.h>

// カスタムヘッダー設定
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/data"},
    cpr::Header{
        {"User-Agent", "MyApp/1.0 (cpr C++)"},
        {"Accept", "application/json"},
        {"Accept-Language", "ja-JP,en-US"},
        {"X-API-Version", "v2"},
        {"X-Request-ID", "req-12345"}
    }
);

// Basic認証
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/private"},
    cpr::Authentication{"username", "password", cpr::AuthMode::BASIC}
);

// Digest認証
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/digest"},
    cpr::Authentication{"username", "password", cpr::AuthMode::DIGEST}
);

// Bearer Token認証(OAuth)
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/protected"},
    cpr::Bearer{"your-jwt-access-token"}
);

// libcurlバージョンによる条件分岐
#if CPR_LIBCURL_VERSION_NUM >= 0x073D00
    // 新しいlibcurlではBearerクラス使用
    cpr::Response r = cpr::Get(
        cpr::Url{"https://api.example.com/bearer"},
        cpr::Bearer{"ACCESS_TOKEN"}
    );
#else
    // 古いlibcurlでは手動でヘッダー設定
    cpr::Response r = cpr::Get(
        cpr::Url{"https://api.example.com/bearer"},
        cpr::Header{{"Authorization", "Bearer ACCESS_TOKEN"}}
    );
#endif

// タイムアウト設定
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/slow"},
    cpr::Timeout{10000}  // 10秒のタイムアウト
);

// プロキシ設定
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/data"},
    cpr::Proxies{{"http", "http://proxy.example.com:8080"},
                {"https", "http://proxy.example.com:8080"}}
);

// 認証付きプロキシ
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/data"},
    cpr::Proxies{{"http", "http://proxy.example.com:8080"}},
    cpr::ProxyAuthentication{{"http", cpr::EncodedAuthentication{"user", "pass"}}}
);

// Cookie設定
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/user-data"},
    cpr::Cookies{{"session_id", "abc123"}, {"user_pref", "dark_mode"}}
);

セッション管理とエラーハンドリング

#include <cpr/cpr.h>
#include <iostream>

// セッションベースAPI(効率的な複数リクエスト)
void sessionExample() {
    cpr::Session session;
    session.SetUrl(cpr::Url{"https://api.example.com"});
    session.SetHeader(cpr::Header{
        {"User-Agent", "MyApp/1.0"},
        {"Accept", "application/json"}
    });
    session.SetAuthentication(cpr::Authentication{"user", "pass", cpr::AuthMode::BASIC});
    
    // 同じセッション設定で複数リクエスト
    cpr::Response users = session.Get(cpr::Url{"https://api.example.com/users"});
    cpr::Response posts = session.Get(cpr::Url{"https://api.example.com/posts"});
    
    // パラメータ変更してリクエスト
    session.SetParameters(cpr::Parameters{{"page", "2"}});
    cpr::Response page2 = session.Get();
}

// 包括的なエラーハンドリング
cpr::Response safeRequest(const std::string& url) {
    cpr::Response r = cpr::Get(cpr::Url{url}, cpr::Timeout{5000});
    
    // エラーチェック
    if (r.error.code != cpr::ErrorCode::OK) {
        std::cerr << "リクエストエラー: " << r.error.message << std::endl;
        return r;
    }
    
    // ステータスコード別処理
    switch (r.status_code) {
        case 200:
            std::cout << "成功: " << r.text.substr(0, 100) << "..." << std::endl;
            break;
        case 401:
            std::cerr << "認証エラー: トークンを確認してください" << std::endl;
            break;
        case 403:
            std::cerr << "権限エラー: アクセス権限がありません" << std::endl;
            break;
        case 404:
            std::cerr << "見つかりません: リソースが存在しません" << std::endl;
            break;
        case 429:
            std::cerr << "レート制限: しばらく待ってから再試行してください" << std::endl;
            break;
        default:
            if (r.status_code >= 500) {
                std::cerr << "サーバーエラー: " << r.status_code << std::endl;
            } else {
                std::cerr << "予期しないステータス: " << r.status_code << std::endl;
            }
    }
    
    return r;
}

// SSL/TLS設定
void sslExample() {
    // 基本的なHTTPS(デフォルトで証明書検証有効)
    cpr::Response r = cpr::Get(cpr::Url{"https://secure-api.example.com/data"});
    
    // クライアント証明書使用
    cpr::SslOptions ssl_opts = cpr::Ssl(
        cpr::ssl::CertFile{"client.pem"},
        cpr::ssl::KeyFile{"client.key"}
    );
    cpr::Response r_cert = cpr::Get(
        cpr::Url{"https://secure-api.example.com/data"}, 
        ssl_opts
    );
    
    // TLSバージョン指定
    cpr::SslOptions tls_opts = cpr::Ssl(cpr::ssl::TLSv1_2{});
    cpr::Response r_tls = cpr::Get(
        cpr::Url{"https://secure-api.example.com/data"}, 
        tls_opts
    );
}

非同期リクエストとマルチリクエスト

#include <cpr/cpr.h>
#include <future>
#include <vector>

// 基本的な非同期リクエスト
void asyncExample() {
    // 非同期GET(Future使用)
    cpr::AsyncResponse future_response = cpr::GetAsync(
        cpr::Url{"https://api.example.com/slow-endpoint"}
    );
    
    // 他の処理を実行
    std::cout << "他の処理を実行中..." << std::endl;
    
    // レスポンス取得(ブロッキング)
    cpr::Response r = future_response.get();
    std::cout << "非同期リクエスト完了: " << r.status_code << std::endl;
    
    // コールバック方式の非同期リクエスト
    auto callback_response = cpr::GetCallback(
        [](cpr::Response r) {
            std::cout << "コールバック実行: " << r.status_code << std::endl;
            return r.text;
        },
        cpr::Url{"https://api.example.com/callback-endpoint"}
    );
}

// マルチリクエスト(並列実行)
void multiRequestExample() {
    cpr::MultiPerform multiperform;
    
    // 複数のセッションを設定
    cpr::Session session1, session2, session3;
    session1.SetUrl(cpr::Url{"https://api.example.com/users"});
    session2.SetUrl(cpr::Url{"https://api.example.com/posts"});
    session3.SetUrl(cpr::Url{"https://api.example.com/comments"});
    
    // マルチパフォーマーに追加
    multiperform.AddSession(&session1);
    multiperform.AddSession(&session2);
    multiperform.AddSession(&session3);
    
    // 並列実行
    std::vector<cpr::Response> responses = multiperform.Get();
    
    // 結果処理
    for (size_t i = 0; i < responses.size(); ++i) {
        std::cout << "レスポンス " << i + 1 << ": " 
                  << responses[i].status_code << std::endl;
    }
}

// 進捗表示付きダウンロード
void downloadWithProgress() {
    cpr::Response r = cpr::Get(
        cpr::Url{"https://api.example.com/large-file.zip"},
        cpr::ProgressCallback([](cpr_off_t downloadTotal, cpr_off_t downloadNow, 
                               cpr_off_t uploadTotal, cpr_off_t uploadNow, 
                               intptr_t userdata) -> bool {
            if (downloadTotal > 0) {
                double progress = (double)downloadNow / downloadTotal * 100.0;
                std::cout << "ダウンロード進捗: " << std::fixed 
                          << std::setprecision(1) << progress << "%\r" << std::flush;
            }
            return true;  // trueで継続、falseでキャンセル
        })
    );
    std::cout << "\nダウンロード完了" << std::endl;
}

ファイルアップロードと実用例

#include <cpr/cpr.h>
#include <fstream>

// ファイルアップロード(マルチパート)
void fileUploadExample() {
    // 単一ファイルアップロード
    cpr::Response r = cpr::Post(
        cpr::Url{"https://api.example.com/upload"},
        cpr::Multipart{
            {"file", cpr::File{"document.pdf"}},
            {"description", "重要書類"},
            {"category", "documents"}
        },
        cpr::Header{{"Authorization", "Bearer your-token"}}
    );
    
    // 複数ファイル + メタデータ
    cpr::Response r_multi = cpr::Post(
        cpr::Url{"https://api.example.com/upload-multiple"},
        cpr::Multipart{
            {"file1", cpr::File{"image1.jpg"}},
            {"file2", cpr::File{"image2.jpg"}},
            {"album_name", "旅行写真"},
            {"public", "false"}
        }
    );
}

// ストリーミングダウンロード
void streamingDownload() {
    std::ofstream file("large-dataset.zip", std::ios::binary);
    
    cpr::Response r = cpr::Download(
        file,
        cpr::Url{"https://api.example.com/files/large-dataset.zip"},
        cpr::Header{{"Authorization", "Bearer your-token"}}
    );
    
    if (r.status_code == 200) {
        std::cout << "ファイルダウンロード完了" << std::endl;
    } else {
        std::cout << "ダウンロードエラー: " << r.status_code << std::endl;
    }
}

// APIクライアントクラス(実用例)
class APIClient {
private:
    cpr::Session session;
    std::string base_url;
    
public:
    APIClient(const std::string& url, const std::string& token) : base_url(url) {
        session.SetHeader(cpr::Header{
            {"Content-Type", "application/json"},
            {"Accept", "application/json"},
            {"User-Agent", "MyApp/1.0 (cpr)"},
            {"Authorization", "Bearer " + token}
        });
    }
    
    cpr::Response get(const std::string& endpoint) {
        session.SetUrl(cpr::Url{base_url + endpoint});
        return session.Get();
    }
    
    cpr::Response post(const std::string& endpoint, const std::string& json_data) {
        session.SetUrl(cpr::Url{base_url + endpoint});
        session.SetBody(cpr::Body{json_data});
        return session.Post();
    }
    
    cpr::Response put(const std::string& endpoint, const std::string& json_data) {
        session.SetUrl(cpr::Url{base_url + endpoint});
        session.SetBody(cpr::Body{json_data});
        return session.Put();
    }
    
    cpr::Response delete_resource(const std::string& endpoint) {
        session.SetUrl(cpr::Url{base_url + endpoint});
        return session.Delete();
    }
};

// 使用例
void apiClientExample() {
    APIClient client("https://api.example.com/v1", "your-jwt-token");
    
    // ユーザー一覧取得
    cpr::Response users = client.get("/users?page=1&limit=10");
    
    // 新しいユーザー作成
    std::string user_data = R"({
        "name": "田中太郎",
        "email": "[email protected]",
        "department": "engineering"
    })";
    cpr::Response created = client.post("/users", user_data);
    
    if (created.status_code == 201) {
        std::cout << "ユーザー作成成功: " << created.text << std::endl;
    }
}