RSS

Archive for the ‘aide-memoire’ Category

C/C++ Constness

Saturday, November 1st, 2025

Constness Post Banner

An aide-memoire post about using the const keyword in function declarations in C/C++. This keyword can have two meanings for pointers depending upon where it is placed:

  • The pointer is constant and cannot be changed
  • The data pointed to by the pointer is constant and cannot be changed

The following application will be used to illustrate the various cases:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void func(char *string)
{
    printf("%s\n", string);
    printf("%s\n", ++string);
    *string = 's';
    printf("%s\n", string);
}

int main(int argc, char**argv)
{
    char *str = strdup("Hello, World!");
    func(str);
    
    free(str);
    return(0);
}

The focus on the definition for func to illustrates the differences. The code fragments that follow will focus on just the definition of func as the rest of the code remains unchanged.

Simple Cases – No const, All const

There are two simple cases:

  • No const – both pointers and data can be changed
  • Both parameters are const – both data and pointer cannot be changed

In the first case, no const, we have the following code:

void func(char *string)
{
    printf("%s\n", string);
    printf("%s\n", ++string);
    *string = 's';
    printf("%s\n", string);
}

Compiling and running this application results in the following output:

Hello, World!
ello, World!
sllo, World!

As we can see, the pointer string and the data pointed to by string can both be changed.

Modifying the code to make make both the pointer and the data pointed to by the pointer string constant, the function definition becomes:

void func(const char * const string)
{
    printf("%s\n", string);
    printf("%s\n", ++string);
    *string = 's';
    printf("%s\n", string);
}

Compiling the application generates the following output:

Const.c:8:20: error: cannot assign to variable 'string' with const-qualified type 'const char *const'
    8 |     printf("%s\n", ++string);
      |                    ^ ~~~~~~
Const.c:5:30: note: variable 'string' declared const here
    5 | void func(const char * const string)
      |           ~~~~~~~~~~~~~~~~~~~^~~~~~
Const.c:9:13: error: read-only variable is not assignable
    9 |     *string = 's';
      |     ~~~~~~~ ^
2 errors generated.

This shows that these lines contain logic errors:

printf("%s\n", ++string);
*string = 's';

This is expected as printf(“%s\n”, ++string); attempts to modify the pointer and *string = ‘s’; attempts to modify the data.

Making the Pointer Constant

The definition of func can be modified to make just the pointer constant and allow the data to be modified:

void func(char * const string)

Compiling the application generates the following output from the compiler:

Const.c:8:20: error: cannot assign to variable 'string' with const-qualified type 'char *const'
    8 |     printf("%s\n", ++string);
      |                    ^ ~~~~~~
Const.c:5:24: note: variable 'string' declared const here
    5 | void func(char * const string)
      |           ~~~~~~~~~~~~~^~~~~~
1 error generated.

Making the Data Constant

There are two ways of allowing the pointer to change wile maintaining the integrity of the data:

void func(const char *string)

and

void func(char const *string)

Compiling an application with either of these function definitions generates and error such as:

Const.c:9:13: error: read-only variable is not assignable
    9 |     *string = 's';
      |     ~~~~~~~ ^
1 error generated.

Function definitions such as the above are useful for functions such as strlen where we want to walk through an array of data in a read-only mode.

Conclusion

This post aims to clear up the confusion about what is constant given that const can appear on both the left and right side of a variable in a function definition. The following table summaraises what is constant for the various options for const:

Definition Description
void func(char *string) Both the data and the data pointed to by string can be changed.
void func(const char * const string)
void func(char const * const string)
The pointer string and the data pointed to by void func(char const * const string) are both constant.
void func(char * const string) The pointer is constant and cannot be changed.
void func(const char *string)
void func(char const *string)
The data pointed to by string is constant.

So the general rule is the item to the left of the const key word is the item that cannot be changed. With one exception and that is when a variables type is prefixed by const. In which case the const applies to the type and not any pointer definition.

Installing Mosquitto MQTT Server

Sunday, April 13th, 2025

Steampunk Mosquito

Recently came across a customer problem which needed access to a MQTT server. Here is how it went.

Requirement

The aim is to provide a cross platform way of providing a MQTT server for testing with the following characteristics:

It should be stressed that this is a disposable test environment running on a local network. The system will not be exposed to the Internet and so security and robustness are not going to be an issue.

  • Cross platform, running on Mac and Raspberry Pi
  • Persistence of data is not necessary as the system will be started and stopped as needed
  • Running on a local network with no access to the Internet (reduced need for security)
  • Simple to configure across platforms

Looking at the above it is apparent that a full installation should not be required. In fact it may be overkill. as would a dedicated server. In fact this use case points in the direction of a docker container with some shared configuration.

Installation and Configuration

Mosquitto is a lightweight MQTT server available as a native application for the target platforms and also as a docker image. Using docker will ensure a consistent deployment across multiple platforms and the Eclipse image will be the one used there.

The docker image can be installed with the command:

docker pull eclipse-mosquitto

The download only takes a few seconds with a moderate speed connection.

The Mosquitto client tools are also required for testing. These are installed with the following commands, for Raspberry Pi:

sudo apt update && sudo apt upgrade
sudo apt install mosquitto-clients

and for Mac (assumes Homebrew is already installed, if not, Homebrew installation instructions can be found here):

brew install mosquitto

Note that on a Mac this installs both the client and the server components although we will only need the client applications for testing the system setup.

The Eclipse image page for the docker container describes a simple directory structure for configuration:

/mosquitto/config
/mosquitto/data
/mosquitto/log

This can be replicated by creating a local directory structure and then mapping this when we start the docker container. The local structure will look like this (note the missing / at the start of the directory names):

mosquitto/config
mosquitto/data
mosquitto/log

Time to run and test the server.

Testing

The client tools will allow the installation to be tested. The server will be installed on a Raspberry Pi with the name testserver500.local. The server is started by logging on to the Raspberry Pi and executing the following command:

docker run -it -p 1883:1883 -v "$PWD/mosquitto/config:/mosquitto/config" -v "$PWD/mosquitto/data:/mosquitto/data" -v "$PWD/mosquitto/log:/mosquitto/log" eclipse-mosquitto

This will start the downloaded image and register the config, data and log directories with the image.

First problem, we also need a configuration file (mosquitto.conf) in the mosquitto/config. A quick scan suggests that the following is all that is required:

persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log

Two open shells are required for testing, one for receiving MQTT messages (subscription) and one for sending MQTT messages (publishing). In the first shell we subscribe to notifications with the command:

mosquitto_sub -h testserver500.local -t test/debug

Second problem, we get an error message Error: Connection refused. Some googling suggests that anonymous login is also required. The configuration file needs a couple more options adding to the file:

persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
allow_anonymous true
listener 1883 0.0.0.0

Stopping the docker container and then restarting it applies the changes. Subscribing again is successful.

Time to try and publish some data. In a third terminal enter the command:

mosquitto_pub -h testserver500.local -t test/debug -m "Hello, world" -d

The subscription terminal should now display the message just sent:

clusteruser@TestServer500:~ $ mosquitto_sub -h testserver500.local -t test/debug
Hello, world

Success!

One final test, publish / subscribe from a remote machine.

Conclusion

Using a docker image makes it possible to use a standardised setup on any platform, including Windows (although this is not in scope here). The only additional set up required is to supply any specialised configuration and possibly data and log directories should data retention be required.

It should also be remembered that the configuration here is really only suitable for a low risk network (i.e. isolated testing systems) and should not be used in production.