C/C++

How to fix C++ linking error: undefined reference to symbol ‘crc32’

Problem:

While trying to compile your C++ program, during the linking stage, you see an error message such as

/bin/ld: minio-cpp/src/utils.o: undefined reference to symbol 'crc32'
/bin/ld: /lib/x86_64-linux-gnu/libz.so.1: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status

Solution:

The crc32 symbol is defined in zlib. Hence, you need to link zlib by adding

-lz

to your linker flags. If you don’t know where your linker flags are, try just adding it to your compiler flags.

Posted by Uli Köhler in C/C++, GCC errors

How to fix C++ “fatal error: INIReader.h: No such file or directory” on Ubuntu

Problem:

While trying to compile your C++ project, you see an error message such as

minio-cpp/include/providers.h:19:10: fatal error: INIReader.h: No such file or directory
   19 | #include <INIReader.h>
      |          ^~~~~~~~~~~~~
compilation terminated.

Solution:

You need to install the inih library. On Ubuntu or Debian, you can do that using

sudo apt -y install libinih-dev

 

Posted by Uli Köhler in C/C++, GCC errors

How to fix C++ “fatal error: curlpp/Easy.hpp: No such file or directory” on Ubuntu

Problem:

While trying to compile your C++ project, you see an error message such as

minio-cpp/include/http.h:21:10: fatal error: curlpp/Easy.hpp: No such file or directory
   21 | #include <curlpp/Easy.hpp>
      |          ^~~~~~~~~~~~~~~~~
compilation terminated.

Solution:

You need to install the curlpp library, a C++ wrapper around libcurl. On Ubuntu or Debian, you can do that using

sudo apt -y install libcurlpp-dev

 

Posted by Uli Köhler in C/C++, GCC errors

How to fix C++ “fatal error: pugixml.hpp: No such file or directory” on Ubuntu

Problem:

While trying to compile your C++ project, you see an error message such as

minio-cpp/include/select.h:19:10: fatal error: pugixml.hpp: No such file or directory
   19 | #include <pugixml.hpp>

Solution:

You need to install the PugiXML library. On Ubuntu or Debian, you can do that using

sudo apt -y install libpugixml-dev

 

Posted by Uli Köhler in C/C++, GCC errors

How to fix C++ error: ‘put_time’ is not a member of ‘std’

Problem:

While trying to compile your C++ application, you see an error message such as

src/HTTPServer.cpp:12:16: error: 'put_time' is not a member of 'std'
     ss << std::put_time(std::localtime(&localTime), "%FT%H-%M-%SZ");;
                ^~~~~~~~

Solution:

At the top of the file where this error occured, you need to add

#include <iomanip>

iomanip contains std::put_time() among other functionality.

Posted by Uli Köhler in C/C++, GCC errors

How to convert std::chrono::time_point to microseconds since epoch

If you have a std::chrono::time_point such as

std::chrono::time_point<std::chrono::system_clock> timepoint = std::chrono::system_clock::now();

You can convert it to microseconds since epoch by first casting it to a chrono::duration using

timepoint.time_since_epoch(); // Returns a duration

and then using std::chrono::duration_cast<std::chrono::milliseconds>(...).count() to convert it to microseconds since epoch and getting the number of intervals (= microseconds) in between epoch and the time point.

std::chrono::duration_cast<std::chrono::microseconds>(timepoint.time_since_epoch()).count()

Full example:

#include <chrono>
#include <iostream>

int main()
{
    auto timepoint = std::chrono::system_clock::now();
    std::cout << "Microseconds since epoch: " <<
        std::chrono::duration_cast<std::chrono::microseconds>(timepoint.time_since_epoch()).count() << std::endl;
}

This prints, for example:

Microseconds since epoch: 1690755479946000
Posted by Uli Köhler in C/C++

How to convert std::chrono::time_point to milliseconds since epoch

If you have a std::chrono::time_point such as

std::chrono::time_point<std::chrono::system_clock> timepoint = std::chrono::system_clock::now();

You can convert it to milliseconds since epoch by first casting it to a chrono::duration using

timepoint.time_since_epoch(); // Returns a duration

and then using std::chrono::duration_cast<std::chrono::milliseconds>(...).count() to convert it to milliseconds since epoch and getting the number of intervals (= seconds) in between epoch and the time point.

std::chrono::duration_cast<std::chrono::milliseconds>(timepoint.time_since_epoch()).count()

Full example:

#include <chrono>
#include <iostream>

int main()
{
    auto timepoint = std::chrono::system_clock::now();
    std::cout << "Milliseconds since epoch: " <<
        std::chrono::duration_cast<std::chrono::milliseconds>(timepoint.time_since_epoch()).count() << std::endl;
}

This prints, for example:

Milliseconds since epoch: 1690755347529

 

Posted by Uli Köhler in C/C++

How to convert std::chrono::time_point to seconds since epoch

If you have a std::chrono::time_point such as

std::chrono::time_point<std::chrono::system_clock> timepoint = std::chrono::system_clock::now();

You can convert it to seconds since epoch by first casting it to a chrono::duration using

timepoint.time_since_epoch(); // Returns a duration

and then using std::chrono::duration_cast<std::chrono::seconds>(...).count() to convert it to seconds since epoch and getting the number of intervals (= seconds) in between epoch and the time point.

std::chrono::duration_cast<std::chrono::seconds>(timepoint.time_since_epoch()).count()

Full example:

#include <chrono>
#include <iostream>

int main()
{
    auto timepoint = std::chrono::system_clock::now();
    std::cout << "Seconds since epoch: " <<
        std::chrono::duration_cast<std::chrono::seconds>(timepoint.time_since_epoch()).count() << std::endl;
}

This prints, for example:

Seconds since epoch: 1690755234

 

Posted by Uli Köhler in C/C++

How to get a std::chrono::time_point representing [now]?

In order to get a std::chrono::time_point<std::chrono::system_clock> representing the current point in time, use std::chrono::system_clock::now()

std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();

Typically, you would use auto to avoid having to type out (or read) the long typename of std::chrono::time_point<std::chrono::system_clock>:

auto now = std::chrono::system_clock::now();

 

Posted by Uli Köhler in C/C++

How to parse datetime in C++ to std::chrono::time_point, trying different formats until success

The following function will try to parse a given string using std::get_time() , trying out a std::vector of different datetime formats until one succeeds.

It will only consider a parsing a success if the entire string has been consumed. Note that this does not mean that the entire pattern has been matched, just that every character of the string has been consumed by the given pattern.

#include <ctime>
#include <iomanip>
#include <sstream>
#include <vector>

/**
 * Parses a string representing a date and time using a list of possible formats,
 * and returns a time point representing that date and time in the system clock's time zone.
 *
 * @param time_str The string to parse.
 * @param formats A vector of possible formats for the string.
 * @return A time point representing the parsed date and time in the system clock's time zone.
 * @throws DatetimeParseFailure if the string cannot be parsed using any of the provided formats.
 */
std::chrono::time_point<std::chrono::system_clock> ParseTimePoint(const std::string& time_str, const std::vector<std::string>& formats) {
    for (const auto& format : formats) {
        std::tm t = {};
        std::istringstream ss(time_str);
        ss >> std::get_time(&t, format.c_str());
        // Only succeed if the entire string was consumed
        if (!ss.fail() && ss.eof()) {
            std::time_t timet = timegm(&t);
            return std::chrono::system_clock::from_time_t(timet);
        }
    }
    throw DatetimeParseFailure("Failed to parse time string");
}

It can be used, for example, with one of the following lists of formats:

const std::vector<std::string> datetimeFormats = {
    "%Y-%m-%d %H:%M:%S",
    "%Y/%m/%d %H:%M:%S",
    "%Y%m%d %H:%M:%S",
    "%Y-%m-%dT%H:%M:%S",
    "%Y/%m/%dT%H:%M:%S"
};

or

const std::vector<std::string> dateFormats = {
    "%Y-%m-%d",
    "%Y/%m/%d",
    "%Y%m%d"
};

 

Posted by Uli Köhler in C/C++

How to parse UTC date/timestamp in C++ to a std::chrono::time_point

The following function parses a string such as

2023-07-04 09:57:01

into a UTC timestamp.

#include <chrono>
#include <sstream>
#include <ctime>

std::chrono::time_point<std::chrono::system_clock> ParseDatetimeFromTimeString(const std::string& timeString) {
    std::tm t = {};
    std::istringstream ss(timeString);
    ss >> std::get_time(&t, "%Y-%m-%d %H:%M:%S");
    if (ss.fail()) {
        throw std::runtime_error("Failed to parse filename time string");
    }
    // Convert timestamp to epoch time assuming UTC
    std::time_t timet = timegm(&t);
    // Convert timet to std::chrono::time_point<std::chrono::system_clock>
    return std::chrono::system_clock::from_time_t(timet);
}

Test code

auto timepoint = ParseDatetimeFromTimeString("2023-07-04 09:57:01");
// print timepoint
std::time_t timet = std::chrono::system_clock::to_time_t(timepoint);
std::cout << std::put_time(std::gmtime(&timet), "%F %T") << std::endl; // Prints 2023-07-04 09:57:01

Since ParseDatetimeFromTimeString() parses the timestamp as UTC, we can use std::put_time(std::gmtime(...)) to print back the original timestamp:

2023-07-04 09:57:01

 

Posted by Uli Köhler in C/C++

How to fix CMake “Could not find a package configuration file provided by “Qt5Core” on Ubuntu?

Problem:

When trying to configure your project using e.g.

cmake .

you see the following error messages:

CMake Warning at CMakeLists.txt:120 (find_package):
  By not providing "FindQt5Core.cmake" in CMAKE_MODULE_PATH this project has
  asked CMake to find a package configuration file provided by "Qt5Core", but
  CMake did not find one.

  Could not find a package configuration file provided by "Qt5Core" with any
  of the following names:

    Qt5CoreConfig.cmake
    qt5core-config.cmake

  Add the installation prefix of "Qt5Core" to CMAKE_PREFIX_PATH or set
  "Qt5Core_DIR" to a directory containing one of the above files.  If
  "Qt5Core" provides a separate development package or SDK, be sure it has
  been installed.

Solution:

Install qtbase5-dev:

sudo apt -y install qtbase5-dev

 

 

Posted by Uli Köhler in C/C++, CMake

How to parse query URLs using ESP-IDF webserver on ESP32

This utility class allows you to easily parse query URLs. In order to use it, you must add -std=gnu++17 or later to your compiler flags.

class QueryURLParser {
public:
    QueryURLParser(httpd_req_t *req) {
        size_t queryURLLen = httpd_req_get_url_query_len(req) + 1;

        if(queryURLLen > 1) {
            // Allocate temporary buffer to store the parameter
            char buf[queryURLLen] = {0};
            if (httpd_req_get_url_query_str(req, buf, queryURLLen) != ESP_OK) {
                ESP_LOGE("Query URL parser", "Failed to extract query URL");
                // Set string to empty string => "not found"
                this->queryURLString = "";
            } else {
                // Copy into a std::string
                this->queryURLString = std::string(buf, queryURLLen);
            }
            delete[] buf;
        }
    }


    std::string GetParameter(const char* key) {
        // Allocate enough space to store the parameter.
        // NOTE: The parameter can only be as long as the query URL itself.
        // Therefore, allocating "too much" space upfront will
        // avoid unneccessary copying
        size_t bufSize = queryURLString.size();
        char* buf = (char*)malloc(bufSize);

        /* Get value of expected key from query string */
        esp_err_t err = httpd_query_key_value(queryURLString.c_str(), key, buf, bufSize);
        if(err != ESP_OK) {
          ESP_LOGE("Query URL parser", "parsing URL");
          Serial.println(esp_err_to_name(err));
          free(buf);
          return "";
        }
        // Convert to std::string
        std::string param(buf);
        free(buf);
        // Shrink param so it fits.
        return param;
    }

private:
    std::string queryURLString;
};

Usage example:

static const httpd_uri_t setMQTTURLHandler = {
    .uri       = "/api/set-mqtt-url",
    .method    = HTTP_GET,
    .handler   = [](httpd_req_t *req) {
        QueryURLParser parser(req);
        std::string url = parser.GetParameter("url");
        if(url.empty()) {
            return SendStatusError(req, "Query parameter 'url' missing");
        }
        // TODO Do something with [url]
        return SendStatusOK(req);
    }
};

SendStatusError() and SendStatusOK() are from our blogpost ESP-IDF webserver: How to respond with JSON success or error message.

Posted by Uli Köhler in C/C++, ESP8266/ESP32

ESP-IDF webserver: How to respond with JSON success or error message

These utility functions allow you to easily respond with a {"status": "ok"} or {"status": "error", "error": "..."}.

Note that the error message is not being escaped but sent as-is, so some error messages containing quotes (") or other special characters might break the JSON.

esp_err_t SendStatusOK(httpd_req_t *request) {
    httpd_resp_set_type(request, "application/json");
    httpd_resp_sendstr(request, "{\"status\":\"ok\"}");
    return ESP_OK;
}

esp_err_t SendStatusError(httpd_req_t *request, const char* description) {
    httpd_resp_set_type(request, "application/json");
    httpd_resp_send_chunk(request, "{\"status\":\"error\", \"error\": \"", HTTPD_RESP_USE_STRLEN);
    // NOTE: We silently assume that description does not have any special characters
    httpd_resp_send_chunk(request, description, HTTPD_RESP_USE_STRLEN);
    httpd_resp_send_chunk(request, "\"}", HTTPD_RESP_USE_STRLEN);
    httpd_resp_send_chunk(request, nullptr, 0); // Finished
    return ESP_OK;
}

 

Posted by Uli Köhler in C/C++, ESP8266/ESP32

Easy-to-use ESP32 IDF webserver C++ wrapper class

#include <esp_http_server.h>

/**
 * HTTP server.
 * Works with ESP-IDF library internally.
 * 
 * Usage:
 * First call http.StartServer()
 * Then call http.RegisterHandler(...) for all handlers
 */
class HTTPServer {
public:
    HTTPServer(): conf(HTTPD_DEFAULT_CONFIG()) {
    }

    void StartServer() {
        if (httpd_start(&server, &conf) != ESP_OK) {
            ESP_LOGE("HTTP server", "Error starting server!");
        }
    }

    void RegisterHandler(const httpd_uri_t *uri_handler) {
        httpd_register_uri_handler(this->server, uri_handler);
    }

    httpd_handle_t server = nullptr;
    httpd_config_t conf;
};

Usage example:

// Declare server globally
HTTPServer http;

// Example handler
static const httpd_uri_t rebootHandler = {
    .uri       = "/api/reboot",
    .method    = HTTP_GET,
    .handler   = [](httpd_req_t *req) {
        SendStatusOK(req);
        delay(20);
        ESP.restart();
        return ESP_OK;
    }
};

void setup() {
    // TODO setup wifi or Ethernet
    http.StartServer();
    http.RegisterHandler(&myHandler);
}

 

Posted by Uli Köhler in C/C++, Embedded, ESP8266/ESP32

How to sort a std::vector of std::pairs

Sort a std::vector<std::pair<...>> in-place by the first element of the pair:

std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) {
    return a.first < b.first;
});

Sort a std::vector<std::pair<...>> in-place by the second element of the pair:

std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) {
    return a.second < b.second;
});

 

Posted by Uli Köhler in C/C++

How to MD5-hash file in C++ using OpenSSL

#include <openssl/md5.h>
#include <openssl/evp.h>
#include <fstream>
#include <iostream>

std::string HashFileMD5(const std::string& filename) {
    std::ifstream file(filename, std::ios::binary);
    if (!file) {
        throw std::runtime_error("Failed to open file: " + filename);
    }

    EVP_MD_CTX* md5Context = EVP_MD_CTX_new();
    EVP_MD_CTX_init(md5Context);
    EVP_DigestInit_ex(md5Context, EVP_md5(), nullptr);

    const size_t bufferSize = 4096;
    char buffer[bufferSize];
    while (!file.eof()) {
        file.read(buffer, bufferSize);
        EVP_DigestUpdate(md5Context, buffer, file.gcount());
    }

    std::array<uint8_t, 16> result;
    EVP_DigestFinal_ex(md5Context, result.data(), nullptr);
    file.close();

    EVP_MD_CTX_free(md5Context);

    return convertToHex(result);
}

 

Posted by Uli Köhler in C/C++, Cryptography

How to get boost::beast HTTP response status code

// Read & parse the response
beast::flat_buffer buffer;
http::response<http::dynamic_body> res;
http::read(stream, buffer, res);

// Print status code, e.g. 200
cout << res.result_int() << endl;

 

Posted by Uli Köhler in Boost, C/C++

How to iterate boost::beast HTTP response headers

// Read & parse the response
beast::flat_buffer buffer;
http::response<http::dynamic_body> res;
http::read(stream, buffer, res);

// Iterate response headers
for (auto it = res.begin(); it != res.end(); ++it) {
    std::cout << it->name_string() << ": " << it->value() << "\n";
}

// ... or get one specific header
cout << res["Content-Length"] << endl;

 

Posted by Uli Köhler in Boost, C/C++