CPR (C++ Requests)
Python RequestsライブラリからインスパイアされたC++HTTPクライアント。libcurlのラッパーとしてモダンC++17のAPIを提供。直感的な構文でHTTPリクエストを構築でき、JSON処理、認証、SSL設定、プロキシサポート等の機能を含む。
GitHub概要
libcpr/cpr
C++ Requests: Curl for People, a spiritual port of Python Requests.
トピックス
スター履歴
ライブラリ
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;
}
}