https://github.com/maximilianfeldthusen/integrate-asynchronous-i-o-using-modern-cpp
Integrate-asynchronous-I-O-using-modern-Cpp
https://github.com/maximilianfeldthusen/integrate-asynchronous-i-o-using-modern-cpp
async cpp io
Last synced: 12 months ago
JSON representation
Integrate-asynchronous-I-O-using-modern-Cpp
- Host: GitHub
- URL: https://github.com/maximilianfeldthusen/integrate-asynchronous-i-o-using-modern-cpp
- Owner: maximilianfeldthusen
- License: mit
- Created: 2025-06-16T09:24:04.000Z (about 1 year ago)
- Default Branch: TFD
- Last Pushed: 2025-06-16T09:32:22.000Z (about 1 year ago)
- Last Synced: 2025-06-16T10:37:50.566Z (about 1 year ago)
- Topics: async, cpp, io
- Language: C++
- Homepage:
- Size: 10.7 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
## Documentation
### Integrate-asynchronous-I-O-using-modern-Cpp
### 1. Header Inclusions and Type Alias
```cpp
#include
#include
#include
#include
#include
#include
#include
```
- **``:** Provides input/output capabilities, allowing the code to print messages to the console.
- **``:** Offers standard exception classes (like `std::runtime_error`), which are used here for error handling.
- **``:** Contains utility functions such as `std::getenv` to access environment variables.
- **``:** Enables the use of C++'s `std::string` for text manipulation.
- **``:** Supports asynchronous operations using `std::async` and `std::future`, letting the code perform a network call without blocking the main thread.
- **``:** Is the header for libcurl, which handles HTTP requests.
- **``:** Is a popular header-only JSON library that makes it easy to build, parse, and manipulate JSON objects in C++.
The statement:
```cpp
using json = nlohmann::json;
```
defines an alias so that we can refer to `nlohmann::json` simply as `json` throughout the code.
---
### 2. The WriteCallback Function
```cpp
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
size_t totalSize = size * nmemb;
std::string* mem = static_cast(userp);
mem->append(static_cast(contents), totalSize);
return totalSize;
}
```
- **Purpose:**
This callback is used by libcurl to process chunks of data received from an HTTP request.
- **How It Works:**
- **Parameters:**
- `contents`: Points to the raw data received.
- `size` **and** `nmemb`: Together they determine the total size of the incoming data (`totalSize = size * nmemb`).
- `userp`: A pointer that we expect to be a `std::string` where the data will be appended.
- **Operation:**
The function casts `userp` to a `std::string*` and appends the new data to it. Finally, it returns the number of bytes processed which tells libcurl that the data was handled correctly.
---
### 3. The `callOpenAICompletionAPI` Function
This function builds and sends an asynchronous HTTP POST request to the OpenAI API and returns the response as a string.
```cpp
std::string callOpenAICompletionAPI(const std::string& prompt) {
// Retrieve the API key securely from an environment variable.
const char* api_key = std::getenv("OPENAI_API_KEY");
if (!api_key) {
throw std::runtime_error("Environment variable OPENAI_API_KEY not set.");
}
```
- **API Key Retrieval:**
It uses `std::getenv` to retrieve the API key from the environment variable `OPENAI_API_KEY`. If the key isn’t found, it throws a runtime error. This keeps sensitive credentials out of the source code.
```cpp
CURL* curl = curl_easy_init();
if (!curl) {
throw std::runtime_error("Failed to initialize CURL.");
}
std::string readBuffer;
CURLcode res;
struct curl_slist* headers = nullptr;
```
- **Initialization:**
- A CURL handle is initialized with `curl_easy_init()`. Failure to initialize results in an exception.
- `readBuffer` is declared to store the response.
- `headers` will hold custom HTTP headers.
#### Inside the Try Block
```cpp
try {
const std::string url = "https://api.openai.com/v1/completions";
json requestData = {
{"model", "text-davinci-003"},
{"prompt", prompt},
{"max_tokens", 150},
{"temperature", 0.7}
};
std::string requestBody = requestData.dump();
```
- **Request Preparation:**
- **URL:** The API endpoint is defined as a constant string.
- **JSON Request:**
A JSON object, `requestData`, is created containing parameters such as the model name, prompt, maximum allowed tokens for the response, and temperature. The JSON object is then converted (serialized) to a string called `requestBody`.
```cpp
// Set CURL options.
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, requestBody.c_str());
```
- **Setting Request Options:**
These options instruct libcurl:
- To use the specified URL.
- To use the POST method.
- To set the body of the POST request to be the JSON string.
```cpp
// Build and set HTTP headers.
std::string authHeader = "Authorization: Bearer " + std::string(api_key);
headers = curl_slist_append(headers, authHeader.c_str());
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
```
- **HTTP Headers:**
- An Authorization header is built including the bearer token.
- A Content-Type header is added to specify the JSON format.
- These headers are then attached to the request.
```cpp
// Register callback for writing received data.
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
```
- **Registering the Callback:**
The callback (`WriteCallback`) is set so that the received data is written into `readBuffer`.
```cpp
// Enforce secure TLS certificate verification.
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
```
- **TLS Security:**
These options enforce verification of the SSL/TLS certificate, ensuring a secure connection.
```cpp
// Execute the HTTP POST request.
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
throw std::runtime_error("CURL error: " + std::string(curl_easy_strerror(res)));
}
```
- **Executing the Request:**
The API call is performed with `curl_easy_perform(curl)`. If any error occurs, an exception is thrown containing the relevant error message.
```cpp
// Cleanup HTTP headers.
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
```
- **Cleanup:**
After the transaction, the allocated headers and CURL session are freed, which helps prevent memory leaks.
#### Exception Handling
```cpp
catch (...) {
if (headers) {
curl_slist_free_all(headers);
}
curl_easy_cleanup(curl);
throw;
}
```
- **Resource Management on Error:**
If an exception is thrown, the catch block ensures that any allocated resources (like headers or the CURL handle) are properly cleaned up before rethrowing the exception.
- **Return Value:**
Finally, `readBuffer`, which now holds the response from the API, is returned.
---
### 4. The `main` Function
```cpp
int main() {
try {
std::string prompt = "Explain best practices for writing secure C++ code with a focus on asynchronous I/O, including memory management, error handling, and secure API design.";
std::cout << "Sending asynchronous request to OpenAI API...\n" << std::endl;
```
- **Defining the Prompt:**
A string `prompt` is defined which contains the question sent to the API.
- **Output:**
A message is printed to indicate that the asynchronous request is being sent.
```cpp
// Launch the API call asynchronously.
std::future futureResponse = std::async(std::launch::async, callOpenAICompletionAPI, prompt);
```
- **Asynchronous Operation:**
The API call is launched asynchronously using `std::async`. This creates a separate thread that will execute `callOpenAICompletionAPI` and returns a `std::future` that will eventually hold the result.
```cpp
// Wait and get the result.
std::string response = futureResponse.get();
```
- **Waiting for the Response:**
Using `futureResponse.get()` blocks until the asynchronous operation completes, then retrieves the response.
```cpp
// Parse and pretty-print the JSON response.
json jsonResponse = json::parse(response);
std::cout << "Asynchronous OpenAI API Response:\n" << jsonResponse.dump(4) << std::endl;
```
- **JSON Parsing:**
The response (assumed to be a JSON-formatted string) is parsed into a JSON object.
- **Pretty Printing:**
The JSON is printed to the console with an indentation of 4 spaces for readability.
```cpp
}
catch (const std::exception& ex) {
std::cerr << "An error occurred: " << ex.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
```
- **Error Handling in `main`:**
Any exceptions thrown during the process are caught, an error message is printed, and the program returns a failure code.
- **Successful Exit:**
If everything runs correctly, the program returns `EXIT_SUCCESS` (typically zero).
---
### Summary
- **Security Practices:**
- Retrieves API keys from environment variables.
- Enforces TLS verification when connecting to the API.
- **Asynchronous Execution:**
- Uses `std::async` and `std::future` to run the network call in a separate thread, keeping the application responsive.
- **Libcurl and JSON:**
- Utilizes libcurl to perform HTTP requests and employs a callback to accumulate the response.
- Uses nlohmann/json to build and parse JSON data effortlessly.
- **Resource Management and Error Handling:**
- Proper management of CURL resources (using cleanup calls) ensures no memory leaks.
- Try-catch blocks gracefully handle errors by releasing resources and providing informative error messages.