Everybody likes to find out new stuff about tools they use. Fortunately, one of our frequently used tools has lots of undocumented and rarely used switches and behaviour that makes it a great candidate for fun discoveries of questionable value. So without further ado, here are seven things that the Visual Studio C++ compiler lets you do, presented in a rough order of potential usefulness.
Using keywords as identifiers
It’s possible to use reserved and key words (those that get coloured blue) as variable names and class names in your programs by wrapping them in __identifier(). The linked MSDN page says that the clr switch is required, but it isn’t, only their example requires it. It doesn’t really serve any purposes except for consuming third-party .net libraries that use C++ keywords in their variables or interfaces.
#include <cstdio> int main() { int __identifier(int) = 8; // int named int, only compiles under c++ printf("%d\n", __identifier(int)); }
Knowing whether a symbol exist
Visual studio allows you to check the compiler’s internal symbol table for types, typedefs, macro, and functions and to do something if what you’re looking for is or isn’t found. It should be noted that even though the constructs use braces they do not introduce a new scope, anything done inside the braces exists at the previous scope level.
__if_exists(CreateWindow) { #pragma message("Included windows.h") // won't show } #include <windows.h> __if_not_exists(ProcessBasicInformation) // these also require c++ { // didn't have definition for NtQueryInformationProcess info level // define it now static const UINT ProcessBasicInformation = 0; ULONG myGlobalLength = 0; } int main() { PEB peb = {0}; NtQueryInformationProcess( GetCurrentProcess(), ProcessBasicInformation, // can use this now knowing it will be defined &peb, sizeof(peb), &myGlobalLength ); }
Redefine basic operations
You know when you learned that when you define operator overloads in C++, at least one of the arguments has to be a user defined type? Well, not anymore. Visual Studio allows you to redefine any of the language’s basic operators, turning your super smart compiler into one that’ll fail first year maths.
// compile with the /d1nonUDToperators switch, you can add this to the additional options in the IDE #include <iostream> int operator+(int a, int b) { return a - b; } /* // beware of code like this, it will recurse indefinitely // there's no way to undo this or get back to the proper behaviour int operator-(int a, int b) { return a - b; } */ int main() { int a = 7, b = 3; std::cout << a << '+' << b << '=' << a + b << '\n'; }
Print macro values
Sometimes it’s nice to know what a macro expands to, but not especially nice to pour over the entire output of the preprocessor, especially if your macro shares part of its name with something quite common. An easier alternative is to use the second form of pragma statement that VS allows, __pragma, instead of the more common #pragma. The advantage to the second one is that it can be used in macros.
#define STRINGIFY_INT(x) #x #define STRINGIFY(x) STRINGIFY_INT(x) #define PRINT_MACRO(x) \ __pragma(message ("Value of macro " #x " is " STRINGIFY(x))) #define MY_MACRO 45 PRINT_MACRO(MY_MACRO) // produces Value of macro MY_MACRO is 45 int main() { return 0; }
Naming threads
You may have when debugging programs using the threadpool or wininet that somethreads have custom names like “Main Thread” that give a little more as to their purpose rather than just the function they started executing from. You can apply these names to your own threads by causing a specific SEH exception in them.
#include <windows.h> #include <process.h> const DWORD MS_VC_EXCEPTION=0x406D1388; #pragma pack(push,8) typedef struct tagTHREADNAME_INFO { DWORD dwType; // Must be 0x1000. LPCSTR szName; // Pointer to name (in user addr space). DWORD dwThreadID; // Thread ID (-1=caller thread). DWORD dwFlags; // Reserved for future use, must be zero. } THREADNAME_INFO; #pragma pack(pop) void SetThreadName(char* threadName, DWORD dwThreadID = (DWORD)-1) { THREADNAME_INFO info; info.dwType = 0x1000; info.szName = threadName; info.dwThreadID = dwThreadID; info.dwFlags = 0; __try { RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); } __except(EXCEPTION_CONTINUE_EXECUTION) { } } unsigned __stdcall ThreadFunc(void*) { SetThreadName("Waiter"); Sleep(20000); } int main() { unsigned threadNum = 0; SetThreadName("Launcher"); intptr_t hThread = _beginthreadex(NULL, 0, &ThreadFunc, NULL, 0, &threadNum); WaitForSingleObject((HANDLE)hThread, INFINITE); CloseHandle((HANDLE)hThread); return 0; }
Timing builds
The IDE provides you a way to see how long the build took to complete. This is fine for casual curiosity but if your project is taking a long time to build and you want to see where the biggest slice of time is going, it’s not so great. Instead of using the IDE option, you can add the /Bt to the compiler command line and you’ll get a much finer breakdown of what’s taking so long. As an example, this can be used to measure the difference between defining and not defining WIN32_LEAN_AND_MEAN
#include <windows.h> #include <cstdio> int main() { printf("Console code page is %d\n", GetConsoleCP()); }
Compiler output:
cl /Ox /GL /Bt /nologo test.cpp /link /LTCG /nologo test2.cpp time(C:\test\Microsoft Visual Studio 10\VC\BIN\amd64\c1xx.dll)=0.518s time(C:\test\Microsoft Visual Studio 10\VC\BIN\amd64\c2.dll)=0.003s Generating code Finished generating code OptRef: Total time = 0.000s OptIcf: Total time = 0.000s Pass 1: Interval #1, time = 0.062s Pass 2: Interval #2, time = 0.016s Final: Total time = 0.078s time(C:\test\Microsoft Visual Studio 10\VC\BIN\amd64\link.exe)=0.102s cl /Ox /GL /Bt /nologo /DWIN32_LEAN_AND_MEAN test2.cpp /link /LTCG /nologo test2.cpp time(C:\test\Microsoft Visual Studio 10\VC\BIN\amd64\c1xx.dll)=0.221s time(C:\test\Microsoft Visual Studio 10\VC\BIN\amd64\c2.dll)=0.002s Generating code Finished generating code OptRef: Total time = 0.000s OptIcf: Total time = 0.015s Pass 1: Interval #1, time = 0.046s Pass 2: Interval #2, time = 0.000s Final: Total time = 0.046s time(C:\test\Microsoft Visual Studio 10\VC\BIN\amd64\link.exe)=0.066s
Extra build info
gcc provides a way to list all the macros it pre-defines for you, unfortunaetly visual studio doesn’t have an option to do just that. You can get a list of all macros used along with all the compiler switches used by adding the /Bz switch to the compiler command line. Add /Bv for in-depth version information.
#include <windows.h> #include <cstdio> int main() { printf("Console code page is %d\n", GetConsoleCP()); }
Compiler output
cl /Ox /GL /Bz /Bv /nologo /c test.cpp Compiler Passes: C:\test\Microsoft Visual Studio 8\VC\BIN\cl.exe: Version 14.00.50727.762 C:\test\Microsoft Visual Studio 8\VC\BIN\c1.dll: Version 14.00.50727.762 C:\test\Microsoft Visual Studio 8\VC\BIN\c1xx.dll: Version 14.00.50727.762 C:\test\Microsoft Visual Studio 8\VC\BIN\c2.dll: Version 14.00.50727.762 C:\test\Microsoft Visual Studio 8\VC\BIN\link.exe: Version 8.00.50727.762 C:\test\Microsoft Visual Studio 8\VC\BIN\mspdb80.dll: Version 8.00.50727.762 C:\test\Microsoft Visual Studio 8\VC\BIN\1033\clui.dll: Version 14.00.50727.762 test.cpp `C:\test\Microsoft Visual Studio 8\VC\BIN\c1xx.dll -zm0xFCDB0000 -il C:\Users\Adrian\AppData\Local\Temp\_CL_cc67db03 -f test.cpp -W 1 -Ze -D_MSC_EXTENSIONS -Zp8 -ZB64 -D_INTEGRAL_MAX_BITS=64 -Gs -Fotest.obj -pc \:/ -Fdvc80.pdb -D_MSC_VER=1400 -D_MSC_FULL_VER=140050727 -D_WIN32 -D_M_IX86=600 -D_M_IX86_FP=0 -GS -GR -D_CPPRTTI -Zc:forScope -Zc:wchar_t -Og -Oi -Ot -Oy -ltcg -Bd -nologo -D_MT -D_DLL -I C:\test\Microsoft Visual Studio 8\VC\ATLMFC\INCLUDE -I C:\test\Microsoft Visual Studio 8\VC\INCLUDE -I C:\test\Microsoft Visual Studio 8\VC\PlatformSDK\include' `C:\test\Microsoft Visual Studio 8\VC\BIN\c2.dll -il C:\Users\Adrian\AppData\Local\Temp\_CL_cc67db03 -f test.cpp -W 1 -Gs4096 -dos -Fotest.obj -Fdvc80.idb -GS -Og -Ob2 -ltcg -Bd -MD' ?/out:test.exe /ltcg /nologo test.obj `"C:\test\Microsoft Visual Studio 8\VC\BIN\link.exe" /link /errorreport:queue @C:\Users\Adrian\AppData\Local\Temp\_CL_1a6fa7falk' Generating code Finished generating code