Tag Archive for Visual C++ 2017

Visual Studio 2017 version 15.8 Preview 3 Released

On June 26, 2018, Microsoft released version 15.8 Preview 3 of Visual Studio 2017. Read the announcement here. A few points worth mentioning:

  • Project Loading: Visual Studio version 15.8 Preview 3 brings faster project reloads for all types of projects. Benefits are huge – project unload and reload that used to take more than a minute for large solutions now takes just a few seconds.
  • C++ Templates IntelliSense: Visual Studio 2017 version 15.8 Preview 3 brings IntelliSense for Templates.
  • C++ Refactoring: A new quick-fix lightbulb to convert basic macros to constexpr as a new tool to modernize your C++ code.
  • Improvements for the C++ code analysis tools.
  • C++ Standards: A new, experimental, token-based preprocessor that conforms to C++11 standards (including C99 preprocessor features), enabled with /experimental:preprocessor switch.

Also interesting to know, since 15.7, Visual C++ conforms with the C++ Standard. Read more about this here.

Share

C++17: Attributes

C++17 introduces three new code attributes:

  • [[fallthrough]]
  • [[maybe_unused]]
  • [[nodiscard]]

The first one was discussed in detail in my C++17: Fallthrough in switch statements blog post. The others are briefly explained below.

[[maybe_unused]]

This attribute can be used to mark variables or functions that might be unused to avoid the compiler from generating a warning for such variables and functions.

For example, suppose you have code as follows:

bool DoSomething()
{
    // ... do something ...
    return true;
}

int main()
{
    bool result = DoSomething();
    assert(result);
}

When you build a release build of your code, the assert() macro becomes a no-operation. As such, in release builds, the result variable is unused, while in debug you need the result variable for the assert(). To avoid the compiler warning, use the [[maybe_unused]] attribute as follows:

int main()
{
    [[maybe_unused]] bool result = DoSomething();
    assert(result);
}

This attribute can also be used on functions. Maybe you have a function that is only executed in debug. In such a case, you can mark the function with the attribute. For example:

static bool OnlyUsedInDebug() { return true; }

int main()
{
    assert(OnlyUsedInDebug());
}

When compiling with gcc, the compiler gives the following warning:

warning: ‘bool OnlyUsedInDebug()’ defined but not used [-Wunused-function]

The avoid this warning, use [[maybe_unused]]:

[[maybe_unused]] static bool OnlyUsedInDebug() { return true; }

[[nodiscard]]

This attribute can be used to make sure the caller of a function does not just discard the return value of that function. For example:

bool DoSomething() { return true; }

int main()
{
    DoSomething();
}

The user is not doing anything with the return value of the DoSomething() function, but the compiler will not give any warning about this. Now, the following code adds the [[nodiscard]] attribute:

[[nodiscard]] bool DoSomething() { return true; }

When you try to call it as in the previous main() function, the compiler will complain with something as follows:

warning C4834: discarding return value of function with ‘nodiscard’ attribute

Share

C++17: std::string_view

If you have to write a function that accepts a string, what type of parameter will you use? You have a couple of choices:

  • const std::string&: downside is that whenever you call the function, an std::string is expected. If you pass a const char*, an std::string instance will be created implicitly for you.
  • const char*: downside is that if your caller has an std::string, they will have to use c_str() on it to pass a const char* to your function.
  • A const char* overload and a const std::string& overload: downside is that you have to implement two functions. This solution quickly becomes unwieldy if you are writing a method that accepts more than one string as parameter. For example, suppose your method requires two strings as parameters. If you want to overload the method to provide all possible combinations of input argument types, then you end up writing four versions: (const char*, const char*), (const char*, const string&), (const string&, const char*), and (const string&, const string&). This only becomes worse with even more string parameters.

C++17 makes it easy by introducing a new type called std::string_view. From now on, if you are writing a function that accepts a string, use an std::string_view as parameter type. No need to use an std::string_view reference. A string_view is very cheap to copy, so it’s perfectly fine to pass by value. Basically, a string_view just contains a pointer to a string, and its length. A string_view parameter accepts any kind of string, such as a C++ std::string, a C-style const char* string, and a string literal, all without any copying involved!

std::string_view is defined in the <string_view> header. It has a similar interface as the well-known std::string class. However, since a string_view is a read-only view of a string, only the const operations of std::string are supported on a string_view. Additionally, a big advantage of a string_view is that it will never copy a string.

Here is a very brief example of its usage:

#include <iostream>
#include <string_view>

using namespace std;

void ProcessString(string_view myString)
{
    cout << myString; if (myString.size() >= 4)
    {
        cout << "   (Substring: " << myString.substr(2, 2) << ")";
    }
    cout << endl;
}

int main()
{
    string myString1 = "Hello";
    const char* myString2 = "C++";
    ProcessString(myString1);  // C++ string
    ProcessString(myString2);  // C-style string
    ProcessString("World!");   // String literal
}

The output of this piece of code is as follows:

Hello   (Substring: ll)
C++
World!   (Substring: rl)

My book, Professional C++, 4th Edition, explains the std::string_view class in a bit more details. Additionally, it explains all new C++17 features, and much more.

Share

C++17: Fallthrough in switch statements

A C++ switch statement allows execution to fall through from one case to the next case when a break statement is missing. For example:

#include <iostream>

using namespace std;

enum class Mode
{
    Default,
    Custom
};

class Parameters {};

Parameters AskUserForCustomParameters()
{
    cout << "Asking user for custom parameters..." << endl;
    return Parameters();
}

void Process(const Parameters& parameters)
{
    cout << "Processing parameters..." << endl;
}

void DoSomething(Mode mode)
{
    Parameters parameters;  // Create a default set of parameters
    switch (mode)
    {
    case Mode::Custom:
        AskUserForCustomParameters();
    case Mode::Default:
        Process(parameters);
        break;
    }
}

int main()
{
    DoSomething(Mode::Custom);

    cout << endl;

    DoSomething(Mode::Default);
}

Of course, in this case we could just have used an if statement, but that wouldn’t demonstrate fallthrough in switch statements.

If you look at the code, the case for Mode::Custom does not contain a break statement. Some compilers will issue a warning for this kind of fallthrough because it might be unintended and a source of bugs. C++17 introduced a [[fallthrough]] attribute that you can use to specify in code that a fallthrough is intentional and should not generate a warning. Here is the modified fragment:

void DoSomething(Mode mode)
{
    Parameters parameters;  // Create a default set of parameters
    switch (mode)
    {
    case Mode::Custom:
        AskUserForCustomParameters();
        [[fallthrough]];
    case Mode::Default:
        Process(parameters);
        break;
    }
}

You don’t need to use the [[fallthrough]] attribute for empty switch cases. No compiler will issue a warning for fallthrough with empty case statements. For example:

enum class Mode
{
    Default,
    Custom,
    Standard
};

void DoSomething(Mode mode)
{
    Parameters parameters;  // Create a default set of parameters
    switch (mode)
    {
    case Mode::Custom:
        AskUserForCustomParameters();
        [[fallthrough]];
    case Mode::Standard:
    case Mode::Default:
        Process(parameters);
        break;
    }
}

My book, Professional C++, 4th Edition, explains all new C++17 features, and much more.

Share

C++17: std::byte

What is it?

C++17 introduced a new type: std::byte.

Previously, when you needed to access raw memory, you would use an unsigned char or a char data type. However, these data types give the impression that you are working with characters or with numeric values. The new std::byte data type does not convey character or arithmetic semantics, it is just a collection of bits. As such, it’s ideal to represent raw memory. An std::byte only supports initialization from an integral type, and can be converted back to an integral type using std::to_integer(). The only other operations supported are bit-wise operations. Both std::byte and std::to_integer() are defined in <cstddef>.

How to use it?

Let’s look at some short examples. First, here is how you can initialize an std::byte:

#include <cstddef>

int main()
{
    std::byte myByte{ 2 };
}

Next, you can use std::to_integer() to convert an std::byte back to an integral type of your choice:

#include <iostream>
#include <cstddef>

int main()
{
    std::byte myByte{ 2 };
    std::cout << std::to_integer<int>(myByte) << std::endl;
}

The following bitwise operations are supported: <<, >>, |, &, ^, ~, and <<=, >>=, |=, &=, and ^=. Here is an example:

#include <iostream>
#include <cstddef>

using namespace std;

void PrintByte(const byte& aByte)
{
    cout << to_integer<int>(aByte) << endl;
}

int main()
{
    byte myByte{ 2 };
    PrintByte(myByte);	// 2

    // A 2-bit left shift
    myByte <<= 2;
    PrintByte(myByte);	// 8

    // Initialize two new bytes using binary literals.
    byte byte1{ 0b0011 };
    byte byte2{ 0b1010 };
    PrintByte(byte1);	// 3
    PrintByte(byte2);	// 10

    // Bit-wise OR and AND operations
    byte byteOr = byte1 | byte2;
    byte byteAnd = byte1 & byte2;
    PrintByte(byteOr);	// 11
    PrintByte(byteAnd);	// 2
}

Why use it?

You might wonder what the difference is with just using the existing uint8_t instead of std::byte. Well, std::byte is really just a bunch of un-interpreted bits. If you use uint8_t, you are actually interpreting the bits as an 8-bit unsigned numerical value, which might convey the wrong semantics. Also, std::byte will not allow accidental arithmetic on it, while uint8_t does.

Raw memory buffers and interoperability with legacy C-style API’s

If you need a buffer of raw memory, then you can use std::vector<std::byte>.

Additionally, if you need to pass such a buffer to a legacy C-style API accepting, for example, an unsigned char*, then you will have to add a cast. Here is a brief example:

void SomeCApi(unsigned char* buffer, unsigned int size)
{
    for (unsigned char index = 0; index < size; ++index) {
        buffer[index] = index;
    }
}

int main()
{
    std::vector<std::byte> buffer{ 100 };
    SomeCApi(reinterpret_cast<unsigned char*>(&buffer[0]), buffer.size());

    for (const auto& element : buffer) { PrintByte(element); }
}

Conclusion

When you are working with raw bits, use an std::byte instead of an unsigned char or char data type.

Share

C++17: Initializers for if & switch statements

Two small, but very useful C++17 features are initializers for if and switch statements. These can be used to prevent polluting the enclosing scope with variables that should only be scoped to the if and switch statement. The for statement already supports such initializers since the beginning.
For example, suppose you are using a map to store the number of times a certain name occurs:

#include <cstddef>
#include <map>
#include <string>
#include <iostream>

int main()
{
    std::map<std::string, size_t> nameCounters{
        {"Marc", 5},
        {"Bob", 12},
        {"John", 3}
    };

    auto result = nameCounters.find("Bob");
    if (result != cend(nameCounters))
        std::cout << "Count: " << result->second << std::endl;
}

In this example I’m searching for a key in the map, and if the key is found, I output the associated value to the console. The result of calling find() is stored in result, but this variable is only used inside the if statement.
With C++17, you can initialize the result variable with an if statement initializer so that the result variable is only known inside the if statement and does not pollute the enclosing scope with unnecessary variables:

#include <cstddef>
#include <map>
#include <string>
#include <iostream>

int main()
{
    // ... Initialize map as before ...

    if (auto result = nameCounters.find("Bob"); result != cend(nameCounters))
        std::cout << "Count: " << result->second << std::endl;
}

Similar kind of initializers are supported for switch statements:

switch (<initializer>; <expression>) { /* ... */ }
Share

C++17: Inline Variables

Before C++17, if your class had any non-const static data members, you had to allocate memory for them. For example, suppose you have the following class definition:

class MyClass
{
private:
    static int s_anInt;
    static std::string s_aString;
};

Then your source file should contain the following:

int MyClass::s_anInt = 42;
std::string MyClass::s_aString = "Hello World!";

This is annoying.
C++17 now supports inline variables which allow you to write the MyClass definition as follows:

class MyClass
{
private:
    static inline int s_anInt = 42;
    static inline std::string s_aString = "Hello World!";
};

This feature makes it easier to write header only classes that contain non-const static data members.

At the time of this writing, Microsoft Visual C++ 2017 does not yet support inline variables.

Share

C++17: Direct vs Copy List Initialization

C++11 introduced the uniform initialization syntax using braced initializer lists. You have direct list initialization which does not include an equal sign, for example:

int myInt{42};

and you have copy list initialization which uses the equal sign, for example:

int myInt = {42};

The auto type deduction rules have changes in C++17. With C++17, if you use copy list initialization, then an initializer_list<> is deduced, for example:

auto initList1 = {42};
auto initList2 = {42, 84, 126};

All values in the braced initializer list must be of the same type. The following does not compile:

auto initList2 = {42, 84.42};

When you use direct list initialization, then a value is deduced. For example, the following deduces an int:

auto anInt{42};

This also means that the following causes a compilation error because more than 1 initialization value is given:

auto ints{42, 84, 126};

This is the new C++17 behavior. With C++11/C++14, all of the above would deduce to an initializer_list<int>.

This changed behavior is included since Microsoft Visual C++ 2015.

Share

C++17: Nested Namespaces

The second post in this series of C++17 features highlights a tiny but very useful new feature called nested namespaces.

Using multi-level namespaces in C++ has always been a pain. Suppose you are writing a game and you have a namespace called Core::Graphics::Rendering. Before C++17 you had to resort to something as follows

namespace Core {
    namespace Graphics {
        namespace Rendering {

            class PostProcessor
            {
            };

        }
    }
}

This leaves you with a very big indentation scheme. You could try to make it look a bit better as follows:

namespace Core { namespace Graphics { namespace Rendering {

    class PostProcessor
    {
    };

}}}

But this does not always plays nice with the auto formatting functionality of your IDE.

Say hello to C++17 nested namespaces. Now you can simply write the following:

namespace Core::Graphics::Rendering {

    class PostProcessor
    {
    };

}

Nested namespaces are supported in Microsoft Visual C++ 2015 since Update 3.

Share

C++17: Structured Bindings

This is a first post in a series of short articles on new C++17 features. These articles will not contain all little details of the new features being presented, but they give you an idea about what new functionality has been added to C++17.

This first article discusses structured bindings. They allow you to declare multiple variables with a single statement that are initialized with values from a pair, tuple, array, or even a struct.

Let’s look at an example using a tuple. Suppose you have the following tuple consisting of a double, string, and integer (note the use of the user-defined std::string literal s):

auto myTuple = std::make_tuple(1.1, "Hello"s, 42);

Before C++17, if you wanted to decompose this tuple into three separate variables, you could use tie() as follows:

double theDouble;
std::string theString;
int theInt;
std::tie(theDouble, theString, theInt) = myTuple;

That’s 4 lines of code just to decompose the tuple.

C++17 structured bindings allow you to do this with a single line:

auto[theDouble, theString, theInt] = myTuple;

It also works for pairs, and this comes in handy for example when using insert() to insert values into a map. insert() returns a pair with as first element an iterator to the inserted element, and as second element a Boolean that is true if the element was inserted successfully. You can decompose that pair as follows:

std::map<int, int> myMap{ {1, 11}, { 2,22 }, { 3,33 }};
auto[it, success] = myMap.insert({ 4,44 });

If you want to use a range-based for loop to iterate over the elements, you could do it as follows:

for (const auto& element : myMap) {
    std::cout << element.first << ": " << element.second << std::endl;
}

The type of the element variable is a pair containing the key and the value of the elements in the map.

Again, you can use structured bindings to make this shorter and easier to understand:

for (const auto&[key, value] : myMap) {
    std::cout << key << ": " << value << std::endl;
}

Structured bindings are available in Microsoft Visual C++ 2017 since Update 3.

Share