-
-
Notifications
You must be signed in to change notification settings - Fork 79
MSVC
This page is meant to serve as a guide to building and developing with MSVC (Microsoft Visual C++).
Currently, we are targeting MSVC 2015 and beyond. The latest MSVC contains many of the C++11 features that we use, and is widely available to users for compiling and developing with.
That said, main development usually targets Clang/GCC, so some features available from those compilers (particularly C99 features) are not available on MSVC. This leads to breaking the MSVC build at times! That said, usually the problem has a pretty nice (but hacky) workaround, so MSVC can still build and run CEmu.
As such, this page is meant to document some of the various quirks that we have found... and fixed.
On Windows, if you are building with Visual Studio, you must use Visual Studio 2015 or newer. You also must download a Qt build that is compatible with Visual Studio 2015 or newer.
- If you don't have Visual Studio 2015 installed, we recommend installing Visual Studio 2015 Community.
- Qt v5.6+ is the minimum version of Qt that supports VS2015. As of writing, the latest Qt version is v5.7.
- Direct download links for Qt v5.7: MSVC 2015 x86 or MSVC 2015 x64
- If you are using Qt v5.6 beta (you shouldn't): Note that the Qt v5.6 beta has a bug where the version of MSVC is incorrect. (It is displayed as 2013 or older.) This is simply a display bug. To ensure that you are using MSVC 2015, check the actual compiler version and ensure that it is 14.0 or newer.
Once you have MSVC 2015 and Qt set up, you have two options:
- Open the .pro file with Qt Creator, set it up (default project settings should be fine), and hit Build
- In a shell, cd to the project folder and type
qmake -r CEmu.pro; make
In order to build the latest CEmu, you need to update your MSVC 2015 to the latest version. Otherwise, you will see crashes related to regex. See: Google search, relevant bug report #1 and #2.
With Qt v5.6 and Qt v5.7, there are some issues when compiling statically. If you are using QtQuick in your application, you may still need to distribute the QtQuick DLLs (with plugin DLLs) with your statically compiled application... which defeats the purpose of compiling it statically. See: Qt issue and rough patches to fix the issue.
Some math constants, like M_1_PI
or M_2_PI
, are available in POSIX math.h
(Linux/Mac, GCC+Clang), but not on MSVC. However, MSVC does include these constants... but you have to explicitly enable them by defining _USE_MATH_DEFINES
. It must be defined as early as possible in order for the constants to be defined. (Hint hint - this should happen before any Qt includes occur!)
Here's a code snip below that we used to fix this issue (within gui/qt/keypad/arrowkey.h
):
/* Enable math constants on MSVC */
#ifdef _MSC_VER
#define _USE_MATH_DEFINES
#endif
chdir
does not exist in MSVC. Of course, MSVC does have the ability to change directories... and they even have the POSIX style function implemented as well! They just like to hide it - _chdir
instead of chdir
. This is sourced from dirent.h
instead of unistd.h
, which doesn't exist on MSVC either.
See this commit for the workaround we used, and the snipplet below:
#ifdef _MSC_VER
#include <direct.h>
#define chdir _chdir
#else
#include <unistd.h>
#endif
GCC/Clang implements the __attribute__((packed))
and __attribute__((aligned(#)))
option, but MSVC does not. That said, MSVC does support packing and alignment, but under different semantics.
We worked around this issue by defining a special macro to perform packing and alignment for all compilers, and simply specifying the right options for MSVC. These macros can be found in core/defines.h and in a snipplet below:
/* Cross-compiler packed wrapper */
#ifdef _MSC_VER
# define PACK(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop))
#elif defined(__GNUC__)
# define PACK(...) __VA_ARGS__ __attribute__((packed))
#endif
/* Cross-compiler alignment */
#if defined(_MSC_VER)
# define ALIGNED_(x) __declspec(align(x))
#elif defined(__GNUC__)
# define ALIGNED_(x) __attribute__ ((aligned(x)))
#endif
You may have seen the magical presentation (specifically 20:18) from Microsoft stating that finally, after many long years, designated initializer support would be arriving in VS2013! Hearing that, you decide to write a very simple snipplet to test drive this new feature:
int main()
{
struct data {
int x;
int y;
int z;
};
struct data x = {
.x = 1,
.y = 2,
.z = 100,
};
return 0;
}
Strangely enough, you see red underlines for the .x
, .y
, and .z
... specifically under the period. You shrug it off, and hit the big compile button. Unfortunately, those red lines were right... and the program does not compile at all! You get:
1>------ Build started: Project: MSVC2015-TestStructBug, Configuration: Debug Win32 ------
1> MSVC2015-TestStructBug.cpp
1>c:\users\win7x64\documents\msvc2015-teststructbug\msvc2015-teststructbug\msvc2015-teststructbug.cpp(10): error C2059: syntax error: '.'
1>c:\users\win7x64\documents\msvc2015-teststructbug\msvc2015-teststructbug\msvc2015-teststructbug.cpp(13): error C2143: syntax error: missing ';' before '}'
1>c:\users\win7x64\documents\msvc2015-teststructbug\msvc2015-teststructbug\msvc2015-teststructbug.cpp(15): error C2059: syntax error: 'return'
1>c:\users\win7x64\documents\msvc2015-teststructbug\msvc2015-teststructbug\msvc2015-teststructbug.cpp(16): error C2059: syntax error: '}'
1>c:\users\win7x64\documents\msvc2015-teststructbug\msvc2015-teststructbug\msvc2015-teststructbug.cpp(16): error C2143: syntax error: missing ';' before '}'
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
You glance at the presentation, and you glance at your code. You are 100% sure that there is nothing wrong. Even when copying the presentation's code, compilation still fails. What is going on? Did Microsoft claim that they supported a feature, and then didn't? Or is there something more devious happening underneath?
After a lot of research, we stumbled upon this Q+A question at MSDN discussing designated initializers. While it didn't look too interesting at first (since it was using a different expression than we were), we found some discussion about something similar to our code example above. And then an interesting revelation - that designated initializers only work in C mode. That is, if you specify /TC
as a command-line option to the compiler, it will compile. That said, enabling C mode will prevent any C++ code from compiling.
Simply said - Microsoft only enabled this nifty C99 feature for those compiling C code. C++ users do not get the feature at all!
Our solution? We (grumpily) replaced designated initializer code with their more verbose counterparts. E.g.,
int main()
{
struct data {
int x;
int y;
int z;
};
struct data x;
x.x = 1,
x.y = 2,
x.z = 100,
/* You could use x = {1,2,100}... but with many fields, this can
* get confusing pretty fast, so we're sticking with the very
* verbose way!
*/
return 0;
}
Pre-VS2015, there was also a bug with initializing structs within structs, or unions within structs. See StackOverflow post here and Microsoft bug report here. According to the StackOverflow post, this bug was first present in VS2013 (when the feature was first implemented), and later fixed in VS2015 CTP 5. Since this was fixed in a preview release (CTP = Consumer Technology Preview), the official releases of VS2015 do not have this issue. Given that we are targeting MSVC 2015, this is not a bug that we need to worry about... or even take advantage of given the limitations above!