Creating wxWidgets Programs with Visual Studio 2017 – Part 2

Part 1 of this post described how to set up your development environment for building wxWidgets-based Windows applications, and how to build a bare-bones application. This post continues the development of this application by modifying it to create a HelloWorld program. Continue reading

Advertisements

Creating wxWidgets Programs with Visual Studio 2017 – Part 1

This post is a replacement for the post of the same name for Visual Studio 2015 that I published approximately two years ago. A number of changes made during various updates to Visual Studio 2017 has invalidated some of the instructions in that earlier post.

I have already discussed building the wxWidgets libraries using Visual Studio 2017. I will assume that you have done that.

Prior to the First Program

Note: This section discusses setting up an environment variable that points to the wxWidgets directory. An alternative, and probably better, method to that given in this section is provided in the User-Wide Settings in Visual Studio 2015 post. If you choose to use the procedure in that post, only set up the User Macro for WXWIN. Do not follow the instructions for adding folders to the Include Directories and the Library Directories. If you do make the changes to the Include Directories and the Library Directories, you may not be able to build the wxWidgets libraries in the future. Continue reading

Using Visual Studio 2017 to Build wxWidgets

About two years ago, I wrote a post outlining how to build wxWidgets using Visual Studio 2015. Since then, wxWidgets has been updated, and Visual Studio is now VS 2017. The procedure I outlined also worked for the first few versions of Visual Studio 2017, but later updates made changes that invalidated some of the steps that I outlined. I also recommended downloading wxWidgets directly from the GitHub code page, a practice that wxWidgets does not recommend.

This post provides a new procedure for building wxWidgets with Visual Studio 2017. It works for wxWidgets 3.0.3, 3.1.0 and hopefully later versions, with Visual Studio 2017 version 15.5.3. Hopefully it will be valid for future updates of Visual Studio as well.

Continue reading

I Wanted to Like C++

I really did. I programmed in C++ 20 plus years ago, first on a Sparc workstation and later on a Windows computer. At the time, it seemed to be a good language, and in fact about the only object oriented language for developing both server and client applications.

But something happened between then and now. That something was the development of a number of programming languages (Java, C#, D, Go, Rust) that tackled the myriad problems that C++ presented.

I will not enumerate the problems that compatibility with C has produced because we are familiar with them, and at least I could live with them. And the C++ standard has advanced enough to provide alternatives for some of these problems (e.g. smart pointers instead of “dumb” pointers). There are still a number of problems that may or will be addressed in future updates to the standard.

Where I have trouble with C++ is mostly with templating. Don’t get me wrong, I think templated classes and methods can be wonderful. Where I have problems is in its other uses: trying to overcome some of the limitations of the language, such as lack of reflection. For an example, have a look at my post titled Compile Time Checking of Class Template Types for how to use SFINAE to determine if a class contains a certain method. This would have been so much easier to do with reflection. Every time I attempt to do any template programming outside of simple class templates, it seems to take days to accomplish.

I am not the only person who has problems with templating and SFINAE. Just pop over to Stack Overflow and do a search for SFINAE. As I write this, there are 2073 questions about it.

There are many changes to the language and standard libraries that would make C++ easier to use, and some of these will be addressed in C++20 and beyond. There are libraries in Boost and elsewhere for a number of these, but their inclusion in the standard libraries will no doubt make changes to those existing APIs.

So I have decided to to look over some of the “replacement” languages: C#, D, Go, and Rust. Each of these languages has the advantage of observing C++ problem areas that arose during at least the first 15 to 30 years of C++ use, and could therefore address in the design of those languages.

Most of the problems could be addressed in C++ except for the stated requirement of backward compatibility between the various C++ standards. Some languages effectively reinvent themselves by introducing new versions that make breaking changes with previous versions; for example: Perl, Python, and D.

Java, C#, D, and Go get around some of C++’s problems by using a garbage collector. Garbage collectors are no-no’s in certain areas of programming where C and C++ are the go-to languages: real-time programming areas such as embedded systems, operating systems, and drivers. But there are some new replacements even in those areas; for example: Rust.

Since my programming efforts are unlikely to involve real-time programming, I can live with the occasional slight slowdown that a garbage collector will cause. At least my limited past experience with Java and C# have shown no problems in that area.

So, I am moving on from C++, and possibly back to a programming language that I have used in the past.

This is the last post on this site, at least for now. You may want to check out my other blogging site, Jim’s Adventures in Programming, for non-C++ related topics. There may not be much there yet, in fact there is nothing as of today, but I will be added content there soon.

Modern Ways of Handling Status and Error Conditions

The post Old School Ways of Handling Status and Error Conditions looked at ways of handling statuses and error conditions in functions and subroutines. Each of these methods worked with pre-C++11 compilers and still work with post-C++11 compilers.

This post will investigate returning statuses and error conditions using functionality that was added in C++11, C++14, and C++17.

std::pair and std::tuple

std::pair is a templated struct that was available priory to C++11, but additional functionality was added in C++11 and C++14, so it is discussed here. Also, std::tuple was added in C++11. A tuple may have any number of elements including 0. std::pair is basically a two-value std::tuple with an additional way of accessing the two values.

Generating a Pair

std::pair<int, vector<float>> foo()
{
    int status {OK};
    vector<float> values {1.0f, 3.14f};
    // do something
    return std::pair<int, vector<float>>(status, values);
}

Alternatively, the return statement in foo could be:

    return std::make_pair(status, values);

make_pair is the preferred method of creating a pair because the value types do not have to be specified; in the constructor, they do.

Even:

    return {status, values};

will work as long as the return type for foo is specified. That is, this final way of generating the pair will not work if foo was declared as:

auto foo();

instead of:

std::pair<int, vector<int>> foo();

 

Generating a Tuple

All of the methods shown above work for generating a std::tuple if you replace pair with tuple. In addition to those, there is one other:

    return std::tie(status, values);

Accessing Values in Pairs and Tuples

There are a number of ways of accessing the values in pairs and tuples. Each subsection below assumes the function foo created in Generating a Pair or Generating a Tuple, above.

first, second

std::pair comes with two member objects, first and second, to retrieve the values in the pair. This is how to use them:

std::pair<int, vector<float>> retValues = foo();
int status = retValues.first;
vector<float> values = retValues.second;
float value1 = retValues.second[1];    // 3.14

There are two weaknesses with first and second:

  1. They only exist for std::pair and not std::tuple; and,
  2. It is difficult at first glance to know what first and second represent.

std::get

Here is how to use std::get:

std::pair<int, vector<float>> retValues = foo();
// retrieve items by index
int status = std::get<0>(retValues);
vector<float> values = std::get<1>(retValues);
float value1 = std::get<1>(retValues)[1];    // 3.14

// retrieve items by type
int newStatus = std::get<int>(retValues);
vector<float> newValues = std::get<vector<float>>(retValues);
float newValue1 = std::get<vector<float>>(retValues)[1];    // 3.14

std::get works with both std::pair and std::tuple. Code that retrieves values by type will only compile if all types in the pair or tuple are different.

As with first and second for std::pair, it is difficult at first glance to know what the various gets represent.

You may want to read Arne Mertz’s post entitled Smelly std::pair and std::tuple where he covers some of the same problem areas.

std::tie

Here is how to use std::tie to retrieve values from pairs or tuples:

int status;
vector<float> values;
std::tie(status, values) = foo();

The types specified for the variables in std::tie must match the types returned by foo or there must be implicit conversions from the types returned by foo to the types of the variables in tie.

Structured Bindings

Structured bindings are new to C++17. Here is how to use structured bindings:

auto [status, values] = foo();

Structured bindings are available in clang 4, and gcc 7. Structured bindings will be recognized as of Visual Studio 2017 Update 3.

std::optional

In Old School Ways of Handling Status and Error Conditions, I discussed special output value. In that case, there is a single return value that indicates that an operation was not successful. Assume you have a configuration file that you read to retrieve the value for some key. Let’s say there are three possible “states” for the key-value pair, each of which has a different meaning:

  1. The key is in the file and has a value set for it.
  2. The key is in the file, but no value is set.
  3. The key is not in the file.

The special output value technique will not handle this because there are two special values. As an example, let’s say that the value is a string. If you return a non-empty string, that is the value that is set. If you return an empty string, does that mean that the key is in the file but has no value set, or that the key is not in the file? Note that I state that a key is in the file but has no value has a different meaning than the key is not in the file.

This is where std::optional is useful. std::optional may or may not contain a value. Using it with the example above, I could return a string containing the value in the file, either the set value or empty. If the optional value is not set, that is an indication that the key is not in the file.

Here is an even simpler example::

std::optional<std::string> optionalFunc(int key)
{
    switch (key) {
        case 1:
            return "key = 1"s;
        case 2:
            return ""s;
        default:
            return {};
    }
}

and how to use it:

 auto opt1 = optionalFunc(1);
 if (opt1) {
     std::cout << "Value returned for key=1: " << opt1.value() << '\n';
 }
 auto opt2 = optionalFunc(2);
 if (opt2.has_value()) {
     std::cout << "Value returned for key=2: " << opt2.value() << '\n';
 }
 auto opt3 = optionalFunc(3);
 if (!opt3) {
     std::cout << "No value returned for key=3\n";
 }

The output is:

Value returned for key=1: key = 1
Value returned for key=2:
No value returned for key=3

std::variant

std::variant is a type-safe union. Here is a simple function that returns a std::variant, either a float value or an int error code:

std::variant<int, float> variantFunc(int key)
{
    switch (key) {
        case 1:
            return 10.0f;
        case 2:
            return 0.0f;
        default:
            return 16; // some error code
    }
}

and here is some code that calls variantFunc:

 for (int key : {1, 2, 3}) {
     auto var = variantFunc(key);
     try {
         float value = std::get<float>(var); // or get<1>(var);
         std::cout << "value for key=" << key << ": " << value << '\n';
    }
    catch (std::bad_variant_access&) {
        int errCode = std::get<int>(var); // or get<0>(var);
        std::cout << "Error code for key=" << key << ": " << errCode << '\n';
    }
 }

Here is the output from this code:

value for key=1: 10
value for key=2: 0
Error code for key=3: 16

Note that if you try to retrieve the wrong type from the variant, a std::bad_variant_access exception is thrown.

Instead of std::get, you could use std::get_if which returns a pointer to the value, or nullptr, so the code above could be written:

for (int key : {1, 2, 3}) {
     auto var = variantFunc(key);
     auto floatPtr = std::get_if<float>(var);
    if(floatPtr != nullptr) {
         std::cout << "value for key=" << key << ": " << *floatPtr << '\n';
    }
    else {
        auto intPtr = std::get_if<int>(var);
        if(intPtr != nullptr) {
            std::cout << "Error code for key=" << key << ": " << *intPtr << '\n';
        }
    }
}

std::any

Similar to std::variant is std::any. In the simple function below, either a float value or an int error code is returned:

std::any anyFunc(int key)
{
    switch (key) {
        case 1:
        case 2:
            return 10.1f * key;
        default:
            return 4; // some error code
    }
}

and here is some code that calls this function and uses the returned value:

 for (int key : {1, 2, 3}) {
     auto var = anyFunc(key);
     try {
         float value = std::any_cast<float>(var);
         std::cout << "value for key=" << key << ": " << value << '\n';
     }
     catch (std::bad_any_cast&) {
         int errCode = std::any_cast<int>(var);
         std::cout << "Error code for key=" << key << ": " << errCode << '\n';
     }
 }

Here is the output from running this code:

value for key=1: 10.1
value for key=2: 20.2
Error code for key=3: 4

Note that std::any_cast will throw a std::bad_any_cast exception if you attempt to cast the std::any value to a type that it does not contain.

Alternatively, you can check the type of the value stored in your std::any variable by calling std::any::type. For example:

for (int key : {1, 2, 3}) {
     auto var = anyFunc(key);
     if(var.type() == typeid(float)) {
         float value = std::any_cast<float>(var);
         std::cout << "value for key=" << key << ": " << value << '\n';
     }
     else if(var.type() == typeid(int)) {
         int errCode = std::any_cast<int>(var);
         std::cout << "Error code for key=" << key << ": " << errCode << '\n';
     }
 }

Summary

Both this post and the Old School Ways of Handling Status and Error Conditions post looked at ways of returning statuses or error codes from a subroutine or function that might also return values. The old school ways still work, but modern C++ (C++11 and later) has provided many more ways of returning either a status or error code, or a valid value.

So, what would I use? For cases where there is a special output value that indicates an operation has failed, then I would use std::optional with no value indicating the failure. For functions where the returned value could be either a valid value or one of a number of different statuses or error codes, then I would use std::pair or std::tuple along with structured bindings (or std::tie until structured bindings are available in Visual C++) to retrieve the values. These object types are the simplest to use; there is no question about what the returned values are, and are the simplest to retrieve the values from. For error conditions where program termination or lengthy recovery procedures are needed, then I would throw an exception to indicate the error condition.