MFC and the future of C++

Josh Samuel
Iress
Published in
8 min readAug 7, 2019

--

https://winworldpc.com/product/turbo-c/3x

History

It was 1992, and Windows was still running as a 16bit operating system layered on top of MS-DOS when Microsoft introduced its Microsoft Foundation Classes (MFC) to developers. Most applications for Windows had previously been written in C, but C++ was starting to take off, and the C++ developers were craving an object-oriented way of developing Windows applications, to match the ease of VB, which had been released a year earlier, but with the flexibility of C / C++.

Back then, Microsoft’s C++ compiler did not have exception handling, templates, nor did it have a standard library of containers and algorithms that took advantage of the C++ language features. As such, MFC used macros to emulate templates and exceptions and created its own set of containers for generic concepts such as strings, arrays, lookup maps and more. Well beyond the brief of object-oriented and easier access the Windows APIs.

Languages and Frameworks have come and gone in the meantime. The Visual Basic language and framework had 6 major iterations before the framework was dumped and the language morphed beyond recognition to use the new .NET Framework that Microsoft started promoting as the answer to everything. Smalltalk was standardized and Java was born, implemented in various use-cases that were ahead of their time (Remember Sun Javastation or Corel Office for Java?), went into and out of favour a few times, and kept on improving. So-called “Fourth Generation languages” were created that were meant to make developers redundant due to English language or drag and drop development (remember Progress 4GL or Powerbuilder?) and the World Wide Web took off with HTML moving to DHTML (if you were using Internet Explorer) and moved quickly through fast iterations to where JavaScript is now the primary language of Web development (and more, think Node).

The rise of the web-native technology stack has made HTML/JavaScript frontend code incredibly fast to prototype, develop and ship. To the point where many locally installed applications now use frameworks such as Electron to power their frontend with web technology, with the feel of a local application. Engineers have blogged passionately about the benefits and overcoming challenges, including from Slack, Microsoft’s VSCode team and the game-changing Hyper terminal. The pace of change achieved here is rapid and hard to refute.

Meanwhile, MFC has stayed on — largely unchanged. Neither hindering nor improving the way we develop our software. Small additions were made to MFC in 2008, to accommodate some new Graphical User Interface designs that Microsoft was promoting — in the form of a “Feature Pack”, that added new classes to allow fancier graphical interfaces on Windows Vista and a couple of years later, internal-only changes to MFC were made to accommodate fancier UI’s Windows 7.

Iress && C++

Back in 1993, when Iress was founded as Dunai Financial Systems in Melbourne, Australia — writing an Equity Information System and Order Management System for stockbrokers, C++ with MFC was the obvious choice. Iress clients ran Windows desktops, and Windows Servers would have been much cheaper than an AS/400 or other mainframe or specialized Unix server choice. This architectural design allowed code to be easily shared between their rich frontend and backend while supporting the low-latency performance requirements demanded by these clients.

Code header comments in the current codebase

Over the years, the C++ codebase has grown at IRESS and served us well in our market data and trading software. We now have millions of lines of C++ code, used in our low latency and high throughput data distribution services, order management software and portfolio management software.

Naturally, our software has changed over the years to stay ahead of the curve with network utilization, efficient usage of compute and to support our international growth. Broadcasts of entire stock market feeds changed to a distributed set of publish/subscribe based services and our trading software evolved to handle both the speed of the global markets as well as broader requirements. As we developed these new services to support the changes, we brought in new technology choices and improved on previous code patterns.

The Boost C++ library was one such choice, giving us more flexible and easier to use data structures and advanced features implemented in cross-platform ways. Multi-index containers, buffer pools, date/time manipulation and more are provided by this library and the boost library is now heavily used in many areas of the Iress C++ codebase.

As we move to the latest generation of compilers, we have been embracing the C++11, C++14 and now C++17 standards — both replacing boost functionality that was standardized as well as allowing new code to be written in ways that are both easier to read and maintain.

Fast forward to today

MFC remains as-is, largely unchanged.

This has resulted in MFC being largely incompatible with Boost, C++11 and beyond. It is also inherently non-portable due to its Windows GUI origin. This ties the code-base down to older coding patterns, older deployment patterns (Windows Desktop), which in turn lowers the Developer Experience (DX) and slows down improvements in quality.

As the C++ language has evolved through, C++98, C++03, C++11, C++14, C++17 and soon C++20, the gap between MFC and best-practice C++ has grown. Some of the significant gaps now include asynchronous functions, lambda functions, iterator based loops, user-defined literals, date/time manipulation, string manipulation and move semantics — to pick just an extremely small subset. C++20 will soon also support improved compartmentalization of large codebases through modules, removing the sprawl of header file interdependencies and improving compiler speed, while the addition of ranges to the C++ library allows for improved code readability. Many people have documented and blogged about these individual improvements, including GitHub repositories of samples, and websites to use as references when trying to implement.

On the GUI side, the gap has grown significantly compared to what is now achievable with HTML/JavaScript, using a fraction of the code. Using modern GUI frameworks, it is possible to deploy new user interface components into a frontend with richer experiences in a fraction of the time it takes to do equivalent widgets in C++, making MFC as a Windows GUI framework deprecated.

The future for your C++ code

As we ponder and progress the way forward for many of our C++ applications into the current world of cloud computing, infrastructure as code and automated deployment pipelines, it’s important to think about what this means for MFC-based software.

Are you considering that automated deployment pipelines and immutable containers require configuration as code — not configuration that is changeable via a GUI? And loading configuration from human-readable files and the environment, rather than from the Windows Registry and ensuring the master version of those files is stored securely and with version history to be used as part of your deployment pipeline. Any local changes become ephemeral — for diagnosis/debugging, not for business as usual use. Once this is done, any and all configuration screens can be deleted.

If you are now able to run in a Windows Server Core container, what benefit is MFC providing? It is limiting you to Windows containers, an immature, though evolving, target with a narrow ecosystem. Can you go that one step further and remove your Windows dependency? Consider then calculating any potential license savings and explore the advanced ecosystem that it will open up. Your business case could possibly tell you to take the next step and get your code running on Linux.

After implementing configuration-as-code and removing all GUI, to get your code running on Linux, the meaty part of the uplift starts:

  1. Replace the MFC provided main window message processing loop with a while loop around your logic in your new main() function. Business logic or signals can break out of the loop.
  2. Replace MFC containers with standard C++ library equivalence (see brief example below)
  3. Update your toolchain to be cross-platform. CMake is a nice cross-platform tool to replace your vcxproj file, supported by Visual Studio on Windows, VSCode and many other development environments.
  4. Choose your target compiler and version. Newer C++ features are only supported in the very latest releases, which may be newer than your default installed versions.

To demonstrate the change, compare the code to setup then iterate over a map in MFC:

CMap<CString, LPCSTR, CBar*, CBar*> myMap;
CBar barFoo(3.1415);
myMap.SetAt("LongBar"), &barFoo);
POSITION pos = myMap.GetStartPosition();
while (pos != NULL)
{
CBar* pBar;
CString string;
// Get key (string) and value (pBar)
myMap.GetNextAssoc(pos, string, pBar);
// Code for the loop goes here
}

Versus the equivalent using C++17 constructs:

std::unordered_map<std::string, CBar> myMap;
myMap.emplace(“LongBar”, 3.1415);
for(auto & [string, bar] : myMap)
{
// Code for the loop goes here
}

The C++17 map is able to own the object, constructing it in place. We can then automatically iterate over the map, taking a named reference to the values for use in the loop. Cognitive load is reduced when reading the function, making it easier to concentrate on the logic that matters most. The mechanical exercise of converting some basic types gives much of this benefit.

Examples of MFC classes and their C++17 equivalents

If you are worried about removing the graphical interface of your applications:

  • Consider the operational speed and quality improvements that you will achieve through passing managed configuration to your software, instead of using your applications local user interface to modify configuration on the servers graphical console.
  • Does it provide the ability to diagnose issues in the software without remoting into the GUI of a machine? Do you have access to your logs and metrics remotely via a SaaS service?
  • Take advantage of having full coverage of operational controls via deterministic configuration and treat this as an enabler for both test coverage and production environment stability.

If you are worried about making wholesale changes to your codebase — so are we. Again there are some possible considerations:

  • What is the expected lifetime of your application? Is it worth a technical investment?
  • Some software may be better off re-written, rather than in-place improving. GUI applications are a prime candidate here. Rewrite the user interface in modern web technology.
  • Applications whose business logic needs unwinding/reworking need careful consideration. You may spend more time fixing introduced bugs than any benefits achieved.
  • Which changes can be done incrementally? What stages of improvement that are appropriate for your application and most importantly, the users of your software.

A lot of software has been written in C++ since it was introduced all those years ago. It’s a strong, high performing language that has been supported and enhanced over time. As the language has changed, so other languages have arisen in prominence, and these changes should cause reflection on what it means for software that may utilise MFC or older versions of the language. At Iress, we’re going through this journey for some of our software. We don’t have all of the answers we need yet, however we do know there will be change!

--

--