Temporary objects: lifetime and Extensions – part 2


This post is centered on “stack-use-after-scope” which is an issue we see often in code review and code sanitizer / valgrind reports. Here’s an example:

struct SimpleCalc{
    SimpleCalc( const std::vector<int>& my_vec ) : 
        m_vec (my_vec) {};

    int calculate() {
        // Uses the m_vec and its content.
    };
private:
    const std::vector<int>& m_vec;
};

// < some code >

// Solution #1
SimpleCalc my_calc( std::vector<int>{ /*some values*/ } ); // <- Problem here. 
// The temp created in the argument will have it's 
// lifetime end with this full expression.
my_calc.calculate(); // Stack-use-after-scope.


The first/simplest solution (let’s call it solution 1) that usually comes to mind is to create an lvalue and pass it to the constructor, as such:

Continue reading “Temporary objects: lifetime and Extensions – part 2”

std::optional – removing confusion around pointer types

std::optional is a useful C++17 feature for knowing when a value is set. There’s been some debate however as to its usefulness for 2 forms.

1. std::optional<bool>
2. std::optional<T*>

Both forms are criticized for being confusing to readers. Considering that this post stems from having to explain recently what it means when std::optional wraps a pointer, I’m inclined to agree. The code below shows where the confusion lies:

std::optional<SomeDataType*> myval;
...
myval = nullptr;

if (myval)
// ?? Is this true or false?

In the highlightedline of the if statement, does will the value evaluate as true or false? It, in fact, does evaluate as true, and the value of operator bool is true. This is where the confusion is though. Some developers see std::optional as a simplified version of a wayt to wrap a pointer, similar to a smartptr. But it is quite different. In this case having set the value, even to nullptr, means the variable is “set”, so to speak. It may not sound very useful, but there are some cases, were knowing if a value is set, where nullptr is a valid value, is useful. The example below of a fast cache that shows that usefulness:

Continue reading std::optional – removing confusion around pointer types

const rvalue references – useful use-cases

Rvalue references are generally used to signal a value that will no longer be used by the caller. Could be a temporary value who’s lifetime will expire when the call returns, or an lvalue which is wrapped with a std::moveto signal it will no longer be used any further. If this isn’t old news, you can review a short description here or with your favorite search engine.

When we bind an rvalue with a function call, the usual form is:


void f(A&& a)
{
   A a1;
   a1.data = a.data;
  
   // rvalue reference a is “moved” from. 
   a.data = nullptr;

   // Note: you'd likely use a swap, 
   // but this is clearer.
}

This is pretty standard stuff. Instead of making a possibly expensive copy of what member variable data contained, we take over the pointer. What this implies is that the parameter a, is modified. The very notion of rvlaue references is based on the idea that we no longer need what was passed as an argumen to the function once the call is returned, and we can extract what we need from it, and leave it in valid, yet unspecified state. But, what if the parameter was set as const in the signature?

void f(const A&& a)

What would this be??? The parameter is const, meaning we can’t extract and “steal” the underlying data in parameter a like we did above. This implies were back to the slow lane of copying if we need to copy and not just access the parameter. Binding wise, a const rvalue can already bind to this overload:

Continue readingconst rvalue references – useful use-cases

Lambda, closures, surprises

Let’s start with a question. Is the following legal (compile + link) C++ code:

const bool myBool1 = [](){ return false; };

If you said yes, it’s legal, and obviously myBool1 is initialized to false. You’re partially right, but you might be surprised. It is in fact, legal code. However, here’s the surprise. The valiable myBool1 is in fact initialized to true. How can that be?

Continue readingLambda, closures, surprises

Anonymous unions

One of the features of C++ is the ability to declare a union, without specifying a name. Here’s an example:

union {
   int a;
   char b;
};

(yes, they can be useful, and I’ll give an example shortly). The standard [class.union.anon / 12.3.1] states the following limitations:

  • Names of members of the anonymous union need to be distinct from any other in the scope of the declared union.
  • Each variable declared in the union will have the same address (C++14)
  • Anonymous unions can’t contain member functions
  • all members are public

The main effect is that the members of a union are defined in scope after the anonymous union is defined. The members may be referenced as though they were any other variable in scope. For example:

int c;
union {
   int a;
   char b;
};

a = 1;
c = 2;

Where it is useful

Tagged unions are an interesting use-case:

Class Tagged{
   union {
      int a;
      char b;
   };

   enum { INT_USED = 1,  CHAR_USED=2 } tag;
};
Continue readingAnonymous unions

When RAII fails you

I’ve already mentioned RAII briefly in this post. But as a recap, RAII is a wonderful concept which allows you to stop worrying about the cleanup. Whether that’s freeing memory, file handlers, of general cleanup.

There are however some cases where RAII won’t work as advertised. In such cases, you don’t pass go, you don’t collect your 200$, and you don’t get the cleanup RAII is meant to do.

The premise of RAII is that as variable goes out of scope, the variable is automatically deleted. This is where we can implemented a custom destructor to handle the cleanup we want. However, for that to happen, the destrucor must be called.

Let’s say we have this piece of code which: Copies a file over to a test area. Runs some tests using the test data in the file. Then once the RAII variable goes out of scope for any reason (exception, normal execution) the file is deleted.

{
	FileCopierRAII fileCopier{ “testFilename” };

	// Modify the file for our test.
	...
	// Run unit tests  
	...
	// Validate results. Call std::abort
 	// if any issues detected. 
	assert( /*Check results*/ );
	
} 
// Temp test file copied over is cleaned
// up automatically here as variable goes
// out of scope.

This looks fairly reasonable at first glance. However the issue lies in what happens within the call to assert. If the assert is true, the cleanup happens, however if the statment passed to assert evaluates to false, then there is no cleanup. How can that be?

When assert fails, it calls std::abort. According to the standard [Termination, paragraph 7]:

Calling the function std::abort() declared in <cstdlib> terminates the program without executing any destructors and without calling the functions passed to std::atexit() or std::at_quick_exit().

The key here is that std::abort does not call the destructors. For a lot cases that may be good enough, however for our case with files being copied over, that’s clearly not the case as the file will not be cleaned up and will linger post termination.

Conclusion: When you you can cases where std::abort or asserts in your code, it’s necessary to handle any cleanup by yourself, without depending on RAII.

Literally: string literals – part 1

String literals is a topic that often comes up when writing critical sections of code. Usually most developers don’t care too much about literals, since in general, they just work. But then you might do some profiling and realize you’re doing excessive amounts of calls to string related functions.

Ordinary string literals (aka narrow string literals) are an array of n const char. A string literal also has static storage duration. A reason why they need to be “const”, is that the standard hints that they may be optimized. Meaning they shouldn’t be modified:


Whether all string literals are distinct (that is, are stored in nonoverlapping objects) and whether successive evaluations of a string-literal yield the same or a different object is unspecified. [ Note: The effect of attempting to modify a string literal is undefined. —end note “

standard [lex.string])
Continue readingLiterally: string literals – part 1

Operator++(int): A case for post-incrementing

If you love writing efficient code, you probably cringe when you see code like this, using post-increment for no gain:

for ( int i = 0; i < max_value; i++ )
{ // do stuff 
}

instead of :

for ( int i = 0; i < max_value; ++i )
{ // do stuff 
}

There are reasons to prefer one over the other. First, let’s start with the signature.

T& T::operator++(); // pre-increment

// vs 

T T::operator++(int); / post-increment
Continue readingOperator++(int): A case for post-incrementing

Temporary objects: lifetime and Extensions

Temporary objects are often created and destroyed through the normal course of a program, without much thought. That’s not entirely true, when writing efficient code, we do try to avoid temporaries when possible. Especially when they involved non trivial constructors and destructors. What I’m referring to when I saw we don’t give much thought to temporaries, is about the lifetime of temporaries (when temporaries can’t be avoided of course). When it comes to most temporaries, they are usually limited to the expression in which they are used. However, there are cases in which the lifetime is extended, some for obvious reasons and some more obscure.

Continue readingTemporary objects: lifetime and Extensions

Range-based for statements: from begin() to end()

One of the new features which makes C++ code much more readable, is the range-based for statements. They can be found in the standard at section [stmt.ranged]. The standard outlines 3 ways to use this new feature. As a braced initializer list, an array, or a class. It’s easy to imagine using a list, array, or other container to iterate over. But what if you want to define a new class, and use it in a range loop? What is the interface your class needs to expose?

Let’s start with braced-init-list. Pretty simple stuff and comes in handy sometimes:

for( auto& chipmunks : 
   { “Alvin”, “Simon”, “Theodore” } )
{
  // Do what chipmunks do. 
}

The array is similar to the above example. The only caveats are the list size must be known, and that the array can’t be composed of incomplete types.

Let’s move on to the interesting part, the iterations over a class. The necessary interface is as simple as a begin() and end(), either exposed as a member function, or as a free function. NOTE: Slightly more than that. The return type of the begin() and end() need to act as a pointer or an iterator.

Continue readingRange-based for statements: from begin() to end()