C/C++

How to Serial.println() a std::string

In Arduino, if you have a std::string:

std::string str = "test";

you can’t directly print it – trying to do so leads to the following error messages:

src/main.cpp: In function 'void setup()':
src/main.cpp:122:22: error: no matching function for call to 'HardwareSerial::println(std::__cxx11::string&)'
   Serial.println(cert);
                      ^
In file included from /home/uli/.platformio/packages/[email protected]2/cores/esp32/Stream.h:26,
                 from /home/uli/.platformio/packages/[email protected]2/cores/esp32/Arduino.h:166,
                 from src/main.cpp:1:
/home/uli/.platformio/packages/[email protected]2/cores/esp32/Print.h:96:12: note: candidate: 'size_t Print::println(const __FlashStringHelper*)'
     size_t println(const __FlashStringHelper *);
            ^~~~~~~
/home/uli/.platformio/packages/[email protected]2/cores/esp32/Print.h:96:12: note:   no known conversion for argument 1 from 'std::__cxx11::string' {aka 'std::__cxx11::basic_string<char>'} to 'const __FlashStringHelper*'
/home/uli/.platformio/packages/[email protected]2/cores/esp32/Print.h:97:12: note: candidate: 'size_t Print::println(const String&)'
     size_t println(const String &s);

 

Solution:

You can use .c_str() to convert it to a NUL-terminated char* which can be printed directly:

Serial.println(str.c_str());

 

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

How to convert Arduino String to std::string

If you have an Arduino String:

String arduinoStr = "test123";

you can easily convert it to a std::string by using:

std::string stdStr(arduinoStr.c_str(), arduinoStr.length());

The std::string constructor will copy the data, therefore you can de-allocate the Arduino String instance safely.

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

How to get filesize in LittleFS (ESP32/PlatformIO)

After you have initialized LittleFS (see ESP32 Filesystem initialization code example (LittleFS)) you can get the filesize by first opening the file, and then calling .size() on the opened file. Don’t forget to close the file afterwards.

auto file = LittleFS.open(filename, "r");
size_t filesize = file.size();
// Don't forget to clean up!
file.close();

Utility function to get the size of a file stored on LittleFS:

size_t LittleFSFilesize(const char* filename) {
  auto file = LittleFS.open(filename, "r");
  size_t filesize = file.size();
  // Don't forget to clean up!
  file.close();
  return filesize;
}

Example usage:

Serial.println(LittleFSFilesize("/cert.pem"));

 

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

How to fix GCC elaborated-type-specifier for a scoped enum must not use the ‘class’ keyword

Problem:

In C++, you are declaring an enum class like this:

enum class Shape : uint8_t {
    Circle = 0,
    Square = 1
};

but when you try to compile the project, you see an error message like

include/Shape.hpp:4:6: warning: elaborated-type-specifier for a scoped enum must not use the 'class' keyword
 enum class Shape : uint8_t {
 ~~~~ ^~~~~
      -----

Solution:

The issue here is that you are deriving the enum class from another type –  uint8_t in this example –  but that type has not been declared.

So the solution is to #include the header where the type from which the enum class is inherited.

In our example – for the type uint8_t and for many other integral types such as int32_t , you can do that by adding

#include <cstdint>

before the enum class declaration.

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

How to fix STM32 error: expected constructor, destructor, or type conversion before __declspec(dllexport)

Problem:

When trying to compile a firmware for an STM32 microcontroller, you see a compiler error message like

myheader.h:23:12: error: expected constructor, destructor, or type conversion before ‘(’ token
   23 |  __declspec(dllexport) int myfunc(

Solution:

We previously explored the same problem for Linux platforms.

__declspec(dllexport) is a Windows-specific feature and not available on non-Windows platform such as the ARM embedded API platform (e.g. STM32). In order to fix it in a compatible way with both Windows and the STM32, add the following code either in a header that is included in every file containing __declspec(dllexport) or add it in each file where the error occurs:

#ifdef __ARM_EABI__
#define __declspec(v)
#endif

This will basically ignore any __declspec() call on the preprocessor level.

By using __ARM_EABI__ specfically, the definition will not trigger for ARM platforms for Windows.

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

How to read ESP32 NVS value into std::string

In our previous post, we discussed How to get the length / size of NVS value on ESP32. Based on that, we can read a NVS value into a std::string.

Strategy

  1. Determine size of value in NVS
  2. Allocate temporary buffer of the determined size
  3. Read value from NVS into temporary buffer
  4. Create std::string from value
  5. Cleanup temporary buffer

Utility function to read NVS value as std::string

In case the key does not exist in NVS, this function will return the empty string ("").

#include <nvs.h>
#include <string>

std::string ReadNVSValueAsStdString(nvs_handle_t nvs, const char* key) {
    /**
     * Strategy:
     *  1. Determine size of value in NVS
     *  2. Allocate temporary buffer of determined size
     *  3. Read value from NVS into temporary buffer
     *  4. Create std::string from value
     *  5. Cleanup
     */
    // Step 1: Get size of key
    esp_err_t err;
    size_t value_size = 0;
    if((err = nvs_get_str(nvs, _key.c_str(), nullptr, &value_size)) != ESP_OK) {
        if(err == ESP_ERR_NVS_NOT_FOUND) {
            // Not found, no error
            return "";
        } else {
            printf("Failed to get size of NVS key %s: %s\r\n", key, esp_err_to_name(err));
            return;
        }
    }
    // Step 2: Allocate temporary buffer to read from
    char* buf = (char*)malloc(value_size);
    // Step 3: Read value into temporary buffer.
    esp_err_t err;
    if((err = nvs_get_str(nvs, _key.c_str(), buf, &value_size)) != ESP_OK) {
        // "Doesn't exist" has already been handled before, so this is an actual error.
        // We assume that the value did not change between reading the size (step 1) and now.
        // In case that assumption is value, this will fail with ESP_ERR_NVS_INVALID_LENGTH.
        // This is extremely unlikely in all usage scenarios, however.
        printf("Failed to read NVS key %s: %s\r\n", key, esp_err_to_name(err));
        free(buf);
        return "";
    }
    // Step 4: Make string
    std::string value = std::string(buf, value_size);
    // Step 5: cleanup
    free(buf);
    
    return value;
}

Usage example

This assumes that you have setup myNvs as we have shown in our previous post How to initialize NVS on ESP32

std::string value = ReadNVSValueAsStdString(myNvs, "MyKey");

C++17 optimizations

Starting from C++17, you can possibly create a std::string directly instead of using the temporary buffer, since there is an overload of .data() that returns a non-const pointer – so you can write directly to the std::string‘s buffer.

However, since my PlatformIO-based toolchain currently doesn’t support that, I have not written that code yet.

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

How to fix C/C++ ‘size_t’ does not name a type

Problem:

When compiling your C or C++ program. you see the following error message:

src/main.cpp:4:11: error: 'size_t' does not name a type
 size_t mySize = 32;

Solution:

In the file where the error occurs (in our example, that would be src/main.cpp), add the following line at or near the top. It must be added before the line where size_t is first used, and usually you would add that line after the last existing #include statements:

#include <stddef.h>

After adding that line, the error should be gone. Possibly you need to add said line to other files as well, so carefully check any compiler error messages if the error re-appears in other files.

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

Minimal boost::asio::serial_port read (read_some) example

The following example shows how to initialize a boost::asio serial_port , set its baud rate, parity & stop bits and then read data from it in a loop, printing the data to stdout as-is.

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

#define BUFSIZE 256

int main() {
    boost::asio::io_service io;
    // Open serial port
    boost::asio::serial_port serial(io, "/dev/ttyUSB0");

    // Configure basic serial port parameters: 115.2kBaud, 8N1
    serial.set_option(boost::asio::serial_port_base::baud_rate(115200));
    serial.set_option(boost::asio::serial_port_base::character_size(8 /* data bits */));
    serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
    serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));

    // Read data in a loop and copy to stdout
    while(true) {
        char data[BUFSIZE];
        size_t n = serial.read_some(boost::asio::buffer(data, BUFSIZE));
        // Write data to stdout
        std::cout.write(data, n);
    }
}

For a more complete example that also shows how to open the serial port, see How to open & initialize boost::asio::serial_port

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

How to set baud rate & parity on boost::asio::serial_port

This example shows how to baudrate, parity & stop bits:

// Configure serial port parameters: 115.2kBaud, 8N1
serial.set_option(boost::asio::serial_port_base::baud_rate(115200));
serial.set_option(boost::asio::serial_port_base::character_size(8 /* data bits */));
serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));

For a more complete example that also shows how to open the serial port, see How to open & initialize boost::asio::serial_port or our Minimal boost::asio::serial_port read (read_some) example

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

How to open & initialize boost::asio::serial_port

This example shows how to open a boost serial port and how to set the baudrate, parity & stop bits:

boost::asio::io_service io;
// Open serial port
boost::asio::serial_port serial(io, "/dev/ttyUSB0");

// Configure basic serial port parameters: 115.2kBaud, 8N1
serial.set_option(boost::asio::serial_port_base::baud_rate(115200));
serial.set_option(boost::asio::serial_port_base::character_size(8 /* data bits */));
serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));

Also see our Minimal boost::asio::serial_port read (read_some) example

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

How to initialize struct sockaddr_in using initializer lists

This way of initializing a struct sockaddr_in uses modern C and avoids all the raw of days that should long be forgotten, at least for most applications. I use this approach successfully e.g. for ESP32 microcontrollers.

struct sockaddr_in server_addr = {
    .sin_family = AF_INET,
    .sin_port = htons(46118),
    .sin_addr = {.s_addr = htonl(INADDR_ANY)}
};

 

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

ESP32 Filesystem initialization code example (LittleFS)

This example code takes care of mounting the filesystem, or creating a filesystem if none is present.

#pragma once
#include <stddef.h>

// InitFilesystem() sets this to true if the filesystem is available.
extern volatile bool filesystemOK;

void InitFilesystem();
#include "FS.h"
#include "LittleFS.h"

volatile bool filesystemOK = false;

void InitFilesystem() {
  // Initialize LittleFS
  if (!LittleFS.begin(false /* false: Do not format if mount failed */)) {
    Serial.println("Failed to mount LittleFS");
    if (!LittleFS.begin(true /* true: format */)) {
      Serial.println("Failed to format LittleFS");
    } else {
      Serial.println("LittleFS formatted successfully");
      filesystemOK = true;
    }
  } else { // Initial mount success
    filesystemOK = true;
  }
}

 

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

How to reverse std::string using std::reverse (C++)

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

int main(int argc, char** argv) {
    std::string s = "My string";
    // This reverses s in-place.
    std::reverse(std::begin(s), std::end(s));
    // Prints "gnirts yM"
    cout << s << endl;
}

 

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

re2 C++ replace (GlobalReplace) minimal example

This minimal example shows how to replace strings using the re2 GlobalReplace() function:

#include <re2/re2.h>
#include <iostream>
#include <string>

using namespace re2;
using namespace std;

// Pre-compiled regex
RE2 myRegex("\\$[^\\$]+\\$");

int main(int argc, char** argv) {
    string myString = "This string $abc123$ contains two formulas $123 != 456$.";
    // NOTE: This will modify myString!
    RE2::GlobalReplace(&myString, myRegex, "FORMULA");
    // Prints "This string FORMULA contains two formulas FORMULA."
    cout << myString << endl;
}

 

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

re2 C++ regular expressions library: How to #include?

#include <re2/re2.h>

using namespace re2;

 

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

cpp-httplib: How to get query parameters

This example shows how to get a query parameter in cpp-httplib. See the cpp-httplib hello example for the full source code:

svr.Get("/hi", [](const httplib::Request& req, httplib::Response& res) {
    std::string id = req.get_param_value("id");
    res.set_content("id=" + id, "text/plain");
});

Access it at http://localhost:8080/hi?id=myid

and it will show you

id=myid

id will be empty (== "") if no such parameter is given.

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

How to fix Paho MQTT undefined reference to `SHA1_Init’

Problem:

When trying to build your C++ application using Paho MQTT, you see a bunch of undefined reference error messages:

MQTTAsync.c:(.text+0x3482): undefined reference to `OpenSSL_version'
/usr/bin/ld: MQTTAsync.c:(.text+0x34a1): undefined reference to `OpenSSL_version'
/usr/bin/ld: MQTTAsync.c:(.text+0x34c0): undefined reference to `OpenSSL_version'
/usr/bin/ld: MQTTAsync.c:(.text+0x34df): undefined reference to `OpenSSL_version'
/usr/bin/ld: MQTTAsync.c:(.text+0x34fe): undefined reference to `OpenSSL_version'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(MQTTAsyncUtils.c.o): in function `MQTTAsync_closeOnly':
MQTTAsyncUtils.c:(.text+0x9b6): undefined reference to `SSL_SESSION_free'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(MQTTAsyncUtils.c.o): in function `MQTTAsync_receiveThread':
MQTTAsyncUtils.c:(.text+0x463b): undefined reference to `SSL_set_session'
/usr/bin/ld: MQTTAsyncUtils.c:(.text+0x5255): undefined reference to `SSL_get1_session'
/usr/bin/ld: MQTTAsyncUtils.c:(.text+0x55bb): undefined reference to `SSL_get1_session'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(SSLSocket.c.o): in function `call_ssl_psk_cb':
SSLSocket.c:(.text+0x11b): undefined reference to `SSL_get_SSL_CTX'
/usr/bin/ld: SSLSocket.c:(.text+0x129): undefined reference to `SSL_CTX_get_ex_data'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(SSLSocket.c.o): in function `SSLSocket_error':
SSLSocket.c:(.text+0x1fa): undefined reference to `SSL_get_error'
/usr/bin/ld: SSLSocket.c:(.text+0x22d): undefined reference to `ERR_print_errors_cb'
/usr/bin/ld: SSLSocket.c:(.text+0x2f1): undefined reference to `ERR_get_error'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(SSLSocket.c.o): in function `SSL_CTX_info_callback':
SSLSocket.c:(.text+0x376): undefined reference to `SSL_alert_desc_string_long'
/usr/bin/ld: SSLSocket.c:(.text+0x381): undefined reference to `SSL_alert_type_string_long'
/usr/bin/ld: SSLSocket.c:(.text+0x3bc): undefined reference to `SSL_get_verify_result'
/usr/bin/ld: SSLSocket.c:(.text+0x411): undefined reference to `SSL_state_string_long'
/usr/bin/ld: SSLSocket.c:(.text+0x461): undefined reference to `SSL_get_current_cipher'
/usr/bin/ld: SSLSocket.c:(.text+0x469): undefined reference to `SSL_CIPHER_get_name'
/usr/bin/ld: SSLSocket.c:(.text+0x474): undefined reference to `SSL_state_string_long'
/usr/bin/ld: SSLSocket.c:(.text+0x4d1): undefined reference to `SSL_alert_desc_string_long'
/usr/bin/ld: SSLSocket.c:(.text+0x4dc): undefined reference to `SSL_alert_type_string_long'
/usr/bin/ld: SSLSocket.c:(.text+0x4e7): undefined reference to `SSL_state_string_long'
/usr/bin/ld: SSLSocket.c:(.text+0x503): undefined reference to `SSL_alert_desc_string_long'
/usr/bin/ld: SSLSocket.c:(.text+0x50e): undefined reference to `SSL_alert_type_string_long'
/usr/bin/ld: SSLSocket.c:(.text+0x541): undefined reference to `SSL_alert_desc_string_long'
/usr/bin/ld: SSLSocket.c:(.text+0x54c): undefined reference to `SSL_alert_type_string_long'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(SSLSocket.c.o): in function `SSLSocket_initialize':
SSLSocket.c:(.text+0x8ee): undefined reference to `OPENSSL_init_ssl'
/usr/bin/ld: SSLSocket.c:(.text+0x90a): undefined reference to `OPENSSL_init_crypto'
/usr/bin/ld: SSLSocket.c:(.text+0x916): undefined reference to `OPENSSL_init_ssl'
/usr/bin/ld: SSLSocket.c:(.text+0x922): undefined reference to `OPENSSL_init_crypto'
/usr/bin/ld: SSLSocket.c:(.text+0xa11): undefined reference to `CRYPTO_get_ex_new_index'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(SSLSocket.c.o): in function `SSLSocket_createContext':
SSLSocket.c:(.text+0xbee): undefined reference to `SSL_CTX_use_certificate_chain_file'
/usr/bin/ld: SSLSocket.c:(.text+0xc48): undefined reference to `SSL_CTX_set_default_passwd_cb'
/usr/bin/ld: SSLSocket.c:(.text+0xc55): undefined reference to `SSL_CTX_set_default_passwd_cb_userdata'
/usr/bin/ld: SSLSocket.c:(.text+0xc67): undefined reference to `SSL_CTX_use_PrivateKey_file'
/usr/bin/ld: SSLSocket.c:(.text+0xc9a): undefined reference to `SSL_CTX_load_verify_locations'
/usr/bin/ld: SSLSocket.c:(.text+0xce5): undefined reference to `SSL_CTX_set_default_verify_paths'
/usr/bin/ld: SSLSocket.c:(.text+0xd2e): undefined reference to `SSL_CTX_set_cipher_list'
/usr/bin/ld: SSLSocket.c:(.text+0xd56): undefined reference to `SSL_CTX_set_ex_data'
/usr/bin/ld: SSLSocket.c:(.text+0xd66): undefined reference to `SSL_CTX_set_psk_client_callback'
/usr/bin/ld: SSLSocket.c:(.text+0xd8f): undefined reference to `SSL_CTX_ctrl'
/usr/bin/ld: SSLSocket.c:(.text+0xdc5): undefined reference to `SSL_CTX_free'
/usr/bin/ld: SSLSocket.c:(.text+0xe51): undefined reference to `SSL_CTX_set_alpn_protos'
/usr/bin/ld: SSLSocket.c:(.text+0xeb9): undefined reference to `TLS_client_method'
/usr/bin/ld: SSLSocket.c:(.text+0xec1): undefined reference to `SSL_CTX_new'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(SSLSocket.c.o): in function `SSLSocket_setSocketForSSL':
SSLSocket.c:(.text+0x1046): undefined reference to `SSL_CTX_set_info_callback'
/usr/bin/ld: SSLSocket.c:(.text+0x1056): undefined reference to `SSL_CTX_set_msg_callback'
/usr/bin/ld: SSLSocket.c:(.text+0x1075): undefined reference to `SSL_new'
/usr/bin/ld: SSLSocket.c:(.text+0x10ab): undefined reference to `SSL_get_cipher_list'
/usr/bin/ld: SSLSocket.c:(.text+0x10be): undefined reference to `SSL_set_fd'
/usr/bin/ld: SSLSocket.c:(.text+0x113b): undefined reference to `SSL_ctrl'
/usr/bin/ld: SSLSocket.c:(.text+0x121c): undefined reference to `SSL_CTX_set_verify'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(SSLSocket.c.o): in function `SSLSocket_connect':
SSLSocket.c:(.text+0x12b7): undefined reference to `ERR_clear_error'
/usr/bin/ld: SSLSocket.c:(.text+0x12bf): undefined reference to `SSL_connect'
/usr/bin/ld: SSLSocket.c:(.text+0x135d): undefined reference to `SSL_get_peer_certificate'
/usr/bin/ld: SSLSocket.c:(.text+0x138c): undefined reference to `X509_check_host'
/usr/bin/ld: SSLSocket.c:(.text+0x13d4): undefined reference to `CRYPTO_free'
/usr/bin/ld: SSLSocket.c:(.text+0x13f6): undefined reference to `X509_free'
/usr/bin/ld: SSLSocket.c:(.text+0x144d): undefined reference to `X509_check_ip_asc'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(SSLSocket.c.o): in function `SSLSocket_getch':
SSLSocket.c:(.text+0x1569): undefined reference to `ERR_clear_error'
/usr/bin/ld: SSLSocket.c:(.text+0x1579): undefined reference to `SSL_read'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(SSLSocket.c.o): in function `SSLSocket_destroyContext':
SSLSocket.c:(.text+0x1630): undefined reference to `SSL_CTX_free'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(SSLSocket.c.o): in function `SSLSocket_close':
SSLSocket.c:(.text+0x16b8): undefined reference to `ERR_clear_error'
/usr/bin/ld: SSLSocket.c:(.text+0x16c1): undefined reference to `SSL_shutdown'
/usr/bin/ld: SSLSocket.c:(.text+0x16ce): undefined reference to `SSL_free'
/usr/bin/ld: SSLSocket.c:(.text+0x16fd): undefined reference to `SSL_CTX_free'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(SSLSocket.c.o): in function `SSLSocket_putdatas':
SSLSocket.c:(.text+0x191f): undefined reference to `ERR_clear_error'
/usr/bin/ld: SSLSocket.c:(.text+0x1930): undefined reference to `SSL_write'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(SSLSocket.c.o): in function `SSLSocket_getdata':
SSLSocket.c:(.text+0x1ca5): undefined reference to `ERR_clear_error'
/usr/bin/ld: SSLSocket.c:(.text+0x1cb8): undefined reference to `SSL_read'
/usr/bin/ld: SSLSocket.c:(.text+0x1d7f): undefined reference to `SSL_pending'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(SSLSocket.c.o): in function `SSLSocket_continueWrite':
SSLSocket.c:(.text+0x1e2f): undefined reference to `ERR_clear_error'
/usr/bin/ld: SSLSocket.c:(.text+0x1e3f): undefined reference to `SSL_write'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(Base64.c.o): in function `Base64_encodeDecode.part.0':
Base64.c:(.text+0x1f): undefined reference to `BIO_f_base64'
/usr/bin/ld: Base64.c:(.text+0x27): undefined reference to `BIO_new'
/usr/bin/ld: Base64.c:(.text+0x2f): undefined reference to `BIO_s_mem'
/usr/bin/ld: Base64.c:(.text+0x37): undefined reference to `BIO_new'
/usr/bin/ld: Base64.c:(.text+0x45): undefined reference to `BIO_push'
/usr/bin/ld: Base64.c:(.text+0x55): undefined reference to `BIO_set_flags'
/usr/bin/ld: Base64.c:(.text+0x78): undefined reference to `BIO_write'
/usr/bin/ld: Base64.c:(.text+0x8e): undefined reference to `BIO_ctrl'
/usr/bin/ld: Base64.c:(.text+0x9e): undefined reference to `BIO_free_all'
/usr/bin/ld: Base64.c:(.text+0xc1): undefined reference to `BIO_read'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(WebSocket.c.o): in function `WebSocket_buildFrame.constprop.0':
WebSocket.c:(.text+0x441): undefined reference to `RAND_bytes'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(WebSocket.c.o): in function `uuid_generate':
WebSocket.c:(.text+0x721): undefined reference to `RAND_bytes'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(WebSocket.c.o): in function `WebSocket_connect':
WebSocket.c:(.text+0x929): undefined reference to `RAND_bytes'
/usr/bin/ld: /home/uli/.conan/data/paho-mqtt-c/1.3.9/_/_/package/6fbd1993993594bd3f9451023a96c5ec2eef0e19/lib/libpaho-mqtt3as.a(WebSocket.c.o): in function `WebSocket_upgrade':
WebSocket.c:(.text+0x1dd9): undefined reference to `SHA1_Init'
/usr/bin/ld: WebSocket.c:(.text+0x1def): undefined reference to `SHA1_Update'
/usr/bin/ld: WebSocket.c:(.text+0x1dff): undefined reference to `SHA1_Final'
collect2: error: ld returned 1 exit status

Solution

In addition to linking the Paho MQTT libraries, you also need to link OpenSSL, i.e. the ssl and crypto libraries. You can link them, for example using

target_link_libraries(myexecutable ssl crypto)

in CMake or by adding these linker flags for GCC:

-lssl -lcrypto

 

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

How to link Paho MQTT C++ binding using CMake

target_link_libraries(myexecutable paho-mqttpp3 paho-mqtt3as ssl crypto)

 

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

How to initialize new C++ executable project using conan

In the directory where you want to create the project, run (obviously, replace mypackageby the name of everywhere in our code!)

conan new mypackage/1.0.0 -s

-s is important here. It tells conan that the source is available in the project directory and it should not be pulled using git etc.

This will initialize a shared library project, which we will now modify to build an executable.

First, open src/CMakeLists.txt and change add_library() to add_executable(), for example

add_library(mypackage mypackage.cpp)

to

add_executable(mypackage mypackage.cpp)

Now we shall modify conanfile.py to properly build & install the executable:

    def package(self):
        self.copy("*.h", dst="include", src="src")
        self.copy("*.lib", dst="lib", keep_path=False)
        self.copy("*.dll", dst="bin", keep_path=False)
        self.copy("*.dylib*", dst="lib", keep_path=False)
        self.copy("*.so", dst="lib", keep_path=False)
        self.copy("*.a", dst="lib", keep_path=False)
    def package_info(self):
        self.cpp_info.libs = ["NoxecoDB"]

with

    def package(self):
        self.copy("mypackage", src="bin", dst="bin", keep_path=False)

    def package_info(self):
        self.env_info.PATH = os.path.join(self.package_folder, "bin")

    def deploy(self):
        self.copy("mypackage", dst="bin", src="bin")

and add

import os.path

to the top of the file

Now let’s add a main() function to  src/mypackage.cpp. Replace the file’s content with

#include <iostream>

int main(int argc, char** argv) {
    std::cout << "Hello World!" <<std::endl;
}

Now it’s time to install the dependencies & build the project using

conan install . && conan build .

The executable will be in

bin/mypackage

You can run it using

./bin/mypackage

which will print

Hello World!

I also recommend to add this .gitignore:

CMakeCache.txt
CMakeFiles/
Makefile
bin/
cmake_install.cmake
conan.lock
conanbuildinfo.cmake
conanbuildinfo.txt
conaninfo.txt
graph_info.json

 

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

How to split strings in C++ using boost::algorithm::split() – a minimal example

#include <boost/algorithm/string/split.hpp> // boost::algorithm::split
#include <boost/algorithm/string/classification.hpp> // boost::is_any_of
#include <string>
#include <iostream>
#include <vector>

int main() {
    std::string mystr = "foo-bar-x";
    // Split by "-"
    std::vector<std::string> splitResult;
    boost::algorithm::split(splitResult, mystr, boost::is_any_of("-"));
    // Print results, one line at a time
    // Prints:
    // foo
    // bar
    // x
    for(const std::string& s : splitResult) {
        std::cout << s << std::endl;
    }
}

 

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