C/C++

How to fix C++ link errors such as “undefined reference to std::filesystem::…”

Problem:

While compiling/linking your C++ project using GCC or Clang you see error messages such as

/usr/bin/ld: CMakeFiles/myproject.dir/main.cpp.o: in function `main':
main.cpp:(.text+0x44d): undefined reference to `std::filesystem::create_directories(std::filesystem::__cxx11::path const&)'
/usr/bin/ld: CMakeFiles/myproject.dir/main.cpp.o: in function `std::filesystem::__cxx11::path::path<char [7], std::filesystem::__cxx11::path>(char const (&) [7], std::filesystem::__cxx11::path::format)':
main.cpp:(.text._ZNSt10filesystem7__cxx114pathC2IA7_cS1_EERKT_NS1_6formatE[_ZNSt10filesystem7__cxx114pathC5IA7_cS1_EERKT_NS1_6formatE]+0x5e): undefined reference to `std::filesystem::__cxx11::path::_M_split_cmpts()'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/myproject.dir/build.make:84: myproject] Error 1
make[1]: *** [CMakeFiles/Makefile2:73: CMakeFiles/myproject.dir/all] Error 2
make: *** [Makefile:84: all] Error 2

Solution:

You need to link the stdc++fs library which comes with your compiler and implements the std::filesystem functionality.

Link it by adding -lstdc++fs to your compiler flags.

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

boost::json How to serialize to std::string minimal example

#include <boost/json.hpp>
#include <fstream>
#include <iostream>

namespace json = boost::json;

int main() {
    // Create a JSON object
    json::object obj{
        {"name", "John Doe"},
        {"age", 30},
        {"city", "New York"}
    };

    // Serialize the JSON object
    std::string serialized = json::serialize(obj);

    // Example of what to do with the serialized JSON
    std::cout << serialized << std::endl;

    return 0;
}

Compile using:

g++ -o main main.cpp -lboost_json

Running the program using ./main will print

{"name":"John Doe","age":30,"city":"New York"}

 

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

boost::json How to serialize to file (std::ofstream) minimal example

#include <boost/json.hpp>
#include <fstream>
#include <iostream>

namespace json = boost::json;

int main() {
    // Create a JSON object
    json::object obj{
        {"name", "John Doe"},
        {"age", 30},
        {"city", "New York"}
    };

    // Open the output file stream
    std::ofstream file("output.json");
    if (file.is_open()) {
        // Serialize the JSON object and write it to the file
        file << obj;

        // Close the file stream
        file.close();
        std::cout << "JSON object serialized and written to file successfully." << std::endl;
    } else {
        std::cout << "Unable to open file for writing." << std::endl;
    }

    return 0;
}

Compile using:

g++ -o main main.cpp -lboost_json

After running the program using ./mainoutput.json will look this this:

{"name":"John Doe","age":30,"city":"New York"}

 

 

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

How to configure CMake to use C++20

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

 

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

C++ equivalent of Python’s os.makedirs(…, exist_ok=True)

In C++17, you can use std::filesystem which provides std::filesystem::create_directories.

Similar to Python’s os.makedirs(..., exist_ok=True) or the shell command mkdir -p, this will recursively create directories.

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main() {
    std::string directoryPath = "output";

    try {
        // Create the directory
        fs::create_directories(directoryPath);
        std::cout << "Directory created successfully." << std::endl;
    } catch (const std::exception& ex) {
        std::cerr << "Error creating directory: " << ex.what() << std::endl;
    }

    return 0;
}

Note that you typically need to tell your compiler to use the C++17 standard. For example, for GCC use  -std=c++17 or -std=gnu++17 in case you want to use GNU extensions

g++ -o main main.cpp -std=c++17

If you have to use an older compiler not supporting C++17 , you might need to use std::experimental::filesystem which basically provides the same API but in the std::experimental::filesystem namespace:

#include <iostream>
#include <experimental/filesystem>

namespace fs = std::experimental::filesystem;

int main() {
    std::string directoryPath = "output";

    try {
        // Create the directory
        fs::create_directories(directoryPath);
        std::cout << "Directory created successfully." << std::endl;
    } catch (const std::exception& ex) {
        std::cerr << "Error creating directory: " << ex.what() << std::endl;
    }

    return 0;
}

 

 

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

How to iterate JSON array in C++ using Boost.JSON

You can use a standard for iteration loon on j.as_array() in order to iterate all values in the given JSON array:

for (auto& value : json.as_array()) {
    std::cout << value.as_int64() << std::endl;
}

Full example:

#include <boost/json.hpp>
#include <iostream>

int main()
{
    const char* json_str = R"([1, 2, 3, 4, 5])";
    boost::json::value j = boost::json::parse(json_str);

    if (j.is_array()) {
        for (auto& value : j.as_array()) {
            std::cout << value.as_int64() << std::endl;
        }
    }

    return 0;
}

Compile using

g++ -o json json.cpp -lboost_json

This will print

1
2
3
4
5

 

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

How to copy boost::urls::url in C++ (Boost.Url)

You can copy a boost::urls::url by using it copy constructor:

boost::urls::url url1 = boost::urls::url::parse("https://example.com/path1");
boost::urls::url url2(url1); // Copy url1 to url2 using the copy constructor

 

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

How to access JSON nested value in C++ using Boost.JSON

#include <boost/json.hpp>

boost::json::value j = /* ... */;
// Access nested value with key "value"
j.at("value").as_string();

Full example:

#include <boost/json.hpp>
#include <iostream>

int main() {
    // Hardcoded JSON string
    const std::string json_str = R"(
        {
            "key": "1234",
            "value": "Hello, world!"
        }
    )";

    // Parse the JSON string
    boost::json::value j = boost::json::parse(json_str);

    // Access the "value" property and print it to stdout
    std::string value = j.at("value").as_string().c_str();
    std::cout << value << std::endl; // Prints "Hello, world!"

    return 0;
}

Compile using

g++ -o json json.cpp -lboost_json
Posted by Uli Köhler in Boost, C/C++

How to parse JSON from std::string in C++ using Boost.JSON

#include <boost/json.hpp>

boost::json::value j = boost::json::parse(json_str);

Full example:

#include <boost/json.hpp>
#include <iostream>

int main() {
    // Hardcoded JSON string
    const std::string json_str = R"(
        {
            "key": "1234",
            "value": "Hello, world!"
        }
    )";

    // Parse the JSON string
    boost::json::value j = boost::json::parse(json_str);

    // Access the "value" property and print it to stdout
    std::string value = j.at("value").as_string().c_str();
    std::cout << value << std::endl; // Prints "Hello, world!"

    return 0;
}

Compile using

g++ -o json json.cpp -lboost_json
Posted by Uli Köhler in Boost, C/C++

How to encode URL query parameters in C++ using Boost.URL

#include <iostream>
#include <boost/url.hpp>

using namespace boost::urls;

int main() {
    std::string username = "myusername";
    std::string password = "mypassword";
    url_view base_url = "https://example.com/api/login";

    url encoded_url(base_url);
    encoded_url.params().set("user name", username); // With space to test proper encoding
    encoded_url.params().set("password", password);

    // Prints "https://example.com/api/login?username=my%20user%20name&password=mypassword"
    std::cout << "Encoded URL: " << encoded_url << std::endl;

    return 0;
}

Compile using

g++ -o urlhost urlhost.cpp -lboost_url

 

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

How to parse host from URL in C++ using Boost::URL

If you have an URL such as:

std::string myURL = "https://subdomain.example.com/api/test";

you can parse the hostname from it using Boost::URL (you need at least Boost 1.81.0, previous versions of boost don’t have Boost.URL) using

boost::urls::url_view url(myURL);
std::string host = url.host();

std::cout << host << std::endl; // Prints "subdomain.example.com"

Full example:

#include <string>
#include <iostream>

#include <boost/url.hpp>

int main() {
    std::string myURL = "https://subdomain.example.com/api/test";

    boost::urls::url_view url(myURL);
    std::string host = url.host();

    std::cout << host << std::endl; // Prints "subdomain.example.com"
}

Compile using

g++ -o urlhost urlhost.cpp -lboost_url

 

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

How to fix GCC fatal error: parquet/arrow/writer.h: No such file or directory

Problem:

While compiling your C++ application, you see an error message like

src/main.cpp:6:10: fatal error: parquet/arrow/writer.h: No such file or directory
    6 | #include <parquet/arrow/writer.h>

Solution:

You need to install the Parquet C++ libraries which are shipped together with the Arrow libraries.

On Ubuntu, you can do that using

sudo apt update
sudo apt install -y -V ca-certificates lsb-release wget
wget https://apache.jfrog.io/artifactory/arrow/$(lsb_release --id --short | tr 'A-Z' 'a-z')/apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
sudo apt install -y -V ./apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
sudo apt update -y
sudo apt -y install libparquet-dev

For other operating systems, see the official Arrow installation guide,

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

How to fix GCC fatal error: arrow/api.h: No such file or directory on Ubuntu

Problem:

While compiling your C++ application, you see an error message like

src/main.cpp:3:10: fatal error: arrow/api.h: No such file or directory
    3 | #include <arrow/api.h>

Solution:

You need to install the Arrow C++ libraries.

On Ubuntu, you can do that using

sudo apt update
sudo apt install -y -V ca-certificates lsb-release wget
wget https://apache.jfrog.io/artifactory/arrow/$(lsb_release --id --short | tr 'A-Z' 'a-z')/apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
sudo apt install -y -V ./apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
sudo apt update -y
sudo apt -y install libarrow-dev

For other operating systems, see the official Arrow installation guide,

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

How to force GCC/G++ to provide colored output

Just add the

-fdiagnostics-color=always

compiler flag

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

Minimal C++ SLIP encoder operating on buffers

The following encoder can be used to encode frames into a byte stream. It operates on complete buffers (i.e. this is not a streaming encoder) but is fast and easy to use.

/*
 * Author: Uli Köhler - techoverflow.net
 * License: CC0 1.0 Universal
 *
 * Description:
 * This is a C++ implementation of a buffer-based SLIP encoder.
 * It can be used to encode frames unambiguously into a stream of bytes.
 * The encoder does not use any memory allocation, but instead requires
 * the caller to provide a buffer for the encoded data - hence being
 * ideally suited for use in embedded systems
 */
#pragma once
#include <cstdint>
#include <cstddef>

#define SLIP_END             ((uint8_t)0300)   // SLIP end of packet character
#define SLIP_ESC             ((uint8_t)0333)   // SLIP escape character
#define SLIP_ESCEND         ((uint8_t)0334)    // Escaped END data byte
#define SLIP_ESCESC         ((uint8_t)0335)    // Escaped ESC data byte

// Return value of slip_encode_packet if the output buffer is not large enough
#define SLIP_ENCODE_ERROR SIZE_MAX // Maximum value of size_t

/**
 * Determine the packet length when sending the given buffer [p] using SLIP.
 * This DOES NOT ENCODE ANYTHING, it just determines the length
 * which an encoded version of the data would have
 * @return The number of bytes a SLIP encoded version of [in] would consume
 */
size_t slip_packet_length(const uint8_t* in, size_t inlen);

/**
 * Encode given input data using SLIP, saving it into the given output buffer.
 * WARNING! The output buffer MUST have a length of at least the return value of
 *    slip_packet_len(in, inlen)
 * In case the output buffer is not large enough, this function will return SLIP_ENCODE_ERROR and
 * the output buffer will be left in an undefined space.
 * Take special care that the input data does not change between the calls to
 * slip_packet_len() and slip_encode_packet()
 * @return The number of bytes in [out], or SLIP_ENCODE_ERROR
 */
size_t slip_encode_packet(const uint8_t* in, size_t inlen, uint8_t* out, size_t outlen);
/*
 * Author: Uli Köhler - techoverflow.net
 * License: CC0 1.0 Universal
 *
 * Description:
 * This is a C++ implementation of a buffer-based SLIP encoder.
 * It can be used to encode frames unambiguously into a stream of bytes.
 * The encoder does not use any memory allocation, but instead requires
 * the caller to provide a buffer for the encoded data - hence being
 * ideally suited for use in embedded systems
 */
#include "SLIPEncoder.hpp"

size_t slip_packet_length(const uint8_t* in, size_t inlen) {
    // This variable contains the length of the data
    size_t outlen = 0;
    const uint8_t* inend = in + inlen; // First character AFTER the
    for (; in < inend ; in++) {
        switch (*in) {
        case SLIP_END: // Need to escape END character to avoid RX seeing end of frame
            // Same as "case SLIP_ESC" so just continue.
        case SLIP_ESC: // Need to escape ESC character, we'll send
            outlen += 2; // Will send ESC + ESCESC
            break;
        default: // Any other character => will just copy
            outlen++; // Will only send the given character
        }
    }
    // + 1: SLIP END bytes
    return outlen + 1;
}

size_t slip_encode_packet(const uint8_t* in, size_t inlen, uint8_t* out, size_t outlen) {
    // This variable contains the length of the data
    uint8_t* out_start = out; // We will increment [out], hence copy the original value.

    // Output buffer must be AT LEAST as long as input data (sanity check)
    if(outlen < inlen) {
        return SLIP_ENCODE_ERROR;
    }

    uint8_t* outend = out + outlen; // First character AFTER the
    const uint8_t* inend = in + inlen; // First character AFTER the
    for (; in < inend ; in++) {
        switch (*in) {
        case SLIP_END: // Need to escape END character to avoid RX seeing end of frame
            // Check out of bounds memory acces
            if(out + 2 >= outend) {
                return SLIP_ENCODE_ERROR;
            }
            // Copy escaped END character
            *out++ = SLIP_ESC;
            *out++ = SLIP_ESCEND;
            break;
        case SLIP_ESC: // Need to escape ESC character, we'll send
            // Check out of bounds memory access
            if(out + 2 >= outend) {
                return SLIP_ENCODE_ERROR;
            }
            // Copy escaped END character
            *out++ = SLIP_ESC;
            *out++ = SLIP_ESCESC;
            break;
        default: // Any other character => just copy the character
            if(out + 1 >= outend) {
                return SLIP_ENCODE_ERROR;
            }
            *out++ = *in;
        }
    }
    // Check out of bounds access for END byte
    if(out + 1 > outend) { // NOTE: > instead of >= since there is only ONE character to be written
        return SLIP_ENCODE_ERROR;
    }
    // Insert END byte
    *out++ = SLIP_END;
    // Return number of bytes
    return (out - out_start);
}

 

 

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

How to fix C++ error: invalid use of template-name ‘std::chrono::time_point’ without an argument list

Problem:

You are trying to use std::chrono::time_point in your C++ code, but the compiler throws an error message like

MyClass.hpp:58:5: error: invalid use of template-name ‘std::chrono::time_point’ without an argument list
   58 |     std::chrono::time_point t0;

Solution:

std::chrono::time_point is a template that requires two template arguments: the clock and the duration. The duration argument, however, defaults to Clock::duration so you only have to explicitly specify the clock.

Typically you can just use std::chrono::system_clock:

std::chrono::time_point<std::chrono::system_clock> t0;

 

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

How to generate filename based on current date & time in C++ using chrono

Problem:

You want to generate a filename, e.g., for datalogging, that contains the current date and time in C++ – for example:

2023-04-22_19-38-34.csv

Solution:

To generate a filename with the current date and time in C++, you can use the chrono library to get the current time and format it as a string. Here’s a code snippet that shows how to do this:

#include <chrono>
#include <iomanip>
#include <sstream>

std::string GetFilenameByCurrentDate(const char* extension=".bin") {
    // Get the current time
    auto now = std::chrono::system_clock::now();

    // Convert to local time
    auto localTime = std::chrono::system_clock::to_time_t(now);

    // Format the timestamp as a string
    std::stringstream ss;
    ss << std::put_time(std::localtime(&localTime), "%F_%H-%M-%S") << extension;
    return ss.str();
}

 

This function takes an optional extension parameter that specifies the file extension to use (by default, “.bin”). It uses the chrono library to get the current time (now), convert it to local time (localTime), and format it as a string (ss). The put_time function is used to format the time as a string in the format “%F_%H-%M-%S”, which represents the date and time in ISO 8601 format (e.g., 2023-04-23_19-13-22).

This will produce a filename such as 2023-04-23_19-13-22.bin, which can be used to create a new file or to store data in an existing file. By including a timestamp in the filename, you can easily keep track of when the file was created and quickly identify files that were created at a particular time.

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

How to fix C++ error: no member named ‘put_time’ in namespace ‘std’

Problem:

You are trying to use the std::put_time function in your C++ application, but you get the following compiler error message:

main.cpp:17:16: error: no member named 'put_time' in namespace 'std'
    ss << std::put_time(std::localtime(&localTime), "%F_%H-%M-%S") << extension;
          ~~~~~^

Solution:

The std::put_time function is part of the <iomanip> header in C++, so you need to include this header in your code to use this function. Add the following code at the top of the file where the error occurs:

#include <iomanip>

Full example:

#include <iostream>
#include <iomanip>
#include <chrono>

int main() {
    auto now = std::chrono::system_clock::now();
    auto localTime = std::chrono::system_clock::to_time_t(now);
    std::cout << std::put_time(std::localtime(&localTime), "%F %T") << std::endl;
    return 0;
}

 

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

How to fix GCC error: implicit instantiation of undefined template ‘std::basic_stringstream<char>’

Problem:

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

main.cpp:15:23: error: implicit instantiation of undefined template 'std::basic_stringstream<char>'
    std::stringstream ss;
                      ^

Solution:

At the top of the file where this error occurs, add the following line:

#include <sstream>

 

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

How to construct C++ chrono::time_point from a milliseconds-since-epoch timestamp

#include <chrono>

size_t myMillisecondsSinceEpochTimestamp = 1680978404374;
std::chrono::time_point<std::chrono::system_clock> myChronoTimestamp(
    std::chrono::milliseconds(myMillisecondsSinceEpochTimestamp)
);

 

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