Skip to main content

C++ Initialization Subtleties (significance of -Werror compiler option)

Modern C++ continuous integration build pipelines might produce huge logs which may be easily overlooked. Among other errors/warnings, a potential risk caused by invalid narrowing assignments might be lurking in those dark corners... This write up is a little reminder about an essential feature in modern C++ compilers and can help defend against that specific problem.

Prior to C++11, the following was a valid assignment and still is even in C++20 or higher ...

unsigned int unsignedSinceBirth = 10;
unsignedSinceBirth = -10;  // assigning a negative value to an unsigned container

printUnsignedVal(unsignedSinceBirth);

which when compiled using these options "g++ -std=c++20 <cpp files> -o <executable-name>" does not even emit a warning. And an executable is generated successfully.

The output of running that code is an arbitrary unexpected value.

Modern C++ (post Cpp11) allow uniform initialization syntax which can help the compiler detect this situation as follows:

unsigned int unsignedSinceBirth = 10;
unsignedSinceBirth = {-10};  // assigning a negative value to an unsigned container
printUnsignedVal(unsignedSinceBirth);

Compiling this revised code fails and that's a good thing. G++ emits this error:

error: narrowing conversion of ‘-10’ from ‘int’ to ‘unsigned int’ [-Wnarrowing]

But when the assignment happens in the following manner, compiler only emits a warning and the compilation still succeeds:

unsigned int unsignedSinceBirth = 10;

// .. somewhere else in the code

int rougeInteger = -10;
unsignedSinceBirth = {rougeInteger}; 
printUnsignedVal(unsignedSinceBirth);

Compiling the revised code does not fail and that's a bad thing. G++ only emits a warning:

warning: narrowing conversion of ‘rougeUnsignedNum’ from ‘int’ to ‘unsigned int’ [-Wnarrowing]

In absence of eagle eyes, that warning will most likely go unnoticed in large build logs and the compilation will succeed. It turns out there is a way to enforce the compiler detect such assignments.

If we include "-Werror" option in the G++ command line as follows:

 "g++ -std=c++20 -Werror <cpp files> -o <executable-name>"

The complier will emit an error and compilation will not be successful and that's the effect we were looking for.

Crux: Always pay attention to what compiler is trying to tell us. If it's not telling, force it to say something :)

Note: Compilation using LLVM clang as follows complains with an error without needing an explicit option:

 "clang -std=c++20 <cpp files> -o <executable-name>"

with the following error:

error: non-constant-expression cannot be narrowed from type 'int' to 'unsigned int' in initializer list [-Wc++11-narrowing] 

Comments

Popular posts from this blog

C++11 std::thread Construction and Assignment Tricks

C++11 arrived with std::thread and related classes built within the standard. With the move semantics in place, the construction and assignment practices of 'std threads' deserve a small write up. Let's define a 'Runnable' first: class Runnable {    public:       virtual void operator ()() = 0;       virtual ~Runnable(){} }; class Task : Runnable {      public:          void operator ()()          {               //do sth here..          } };                                                 //later..      #include <thread>     Task runnable;     std::thread t1; //not attached to any thread                                                 std::thread t2 ( runnable ); //attached and running   Copy Construction of std::thread is not allowed            std::thread t3 ( t2 ); // compilation error            std::thread t4 = t2; // compilation error error : use of deleted function std::thread (const std::th

C++ logging using Apache Log4cxx on Linux

I'd demonstrate the set up of Apache log4cxx on Ubuntu 12.x in following steps: 1. Download all required packages 2. Build Log4cxx via Apache Maven 3. Use the libraries in a NetBeans C++ project on Ubuntu Linux. Reference URL: http://www.yolinux.com/TUTORIALS/Log4cxx.html This URL is quite detailed and explains things for other *nix operating systems as well. I wanted to start with minimum steps required, hence this post. I have a Windows 7 desktop and have Ubuntu 12.x 64-bit running on it via Oracle Virtualbox. So Ubuntu is running as a guest on my Windows 7 host. [The reference URL mentions different versions of  'libaprXX' libs but we have to use 'libapr1-dev' and 'libaprutil-dev', will see that later.] Prerequisites (install all of the following one by one) autoconf and automake --> sudo apt-get install autoconf automake libxml2 and libxml2-devel ( http://xmlsoft.org/ ) --> sudo apt-get install libxml2 libxml2-dev gmp (

..Where Apache Camel meets Spring JMS

This post is relevant for developers who are aware of Apache Camel and use its JMS component to consume from a JMS broker. We are only discussing the message consumption part here. I'm assuming you are conversant with Camel's terminology of routes, endpoints, processors and EIPs. If not, following is a good place to start: Link --->  Apache Camel Home   Ever wondered what happens underneath when a Camel consumer route is defined to take messages from a JMS endpoint ? This post is an attempt to explain how Camel integrates with JMS via Spring APIs and how the business logic defined in a Camel route is invoked ? Following is a simplified sequence diagram showing the set-up process which will eventually lead to message consumption from JMS broker. . Creation of JMS endpoints in a camel context is initiated by definition (via Java/XML DSL). A JMS endpoint takes a number of configurable attributes as seen here Camel JMS Component . As soon as context is bootstrappe