A. Hue A. Hue - 12 days ago 6
C++ Question

Why is the order of #include statements so important?

In my program (wxWidgets, code::blocks), I noticed some behaviour I don't quite understand. If I write my header like this:

#ifndef RECORDTHREAD_H
#define RECORDTHREAD_H

#include <wx/thread.h>
#include <wx/dialog.h>
#include <wx/string.h>

#include "Serial.h"

class RecordTrackDialog;

class RecordThread : public wxThread
{
public:
RecordThread(RecordTrackDialog* parent);
virtual ~RecordThread();
protected:
private:
virtual ExitCode Entry();

Serial m_serial;
};

#endif // RECORDTHREAD_H


(with
#include "Serial.h"
as last include statement) all works fine, though when I change the include statements like this:

#ifndef RECORDTHREAD_H
#define RECORDTHREAD_H

#include "Serial.h"

#include <wx/thread.h>
#include <wx/dialog.h>
#include <wx/string.h>


I get errors like this:

||=== Build: Debug in WindowsDgpsGUI (compiler: GNU GCC Compiler) ===|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h||In function 'HWND__* CreateDialog(HINSTANCE, LPCTSTR, HWND, DLGPROC)':|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h|38|error: cannot convert 'LPCTSTR {aka const char*}' to 'LPCWSTR {aka const wchar_t*}' for argument '2' to 'HWND__* CreateDialogParamW(HINSTANCE, LPCWSTR, HWND, DLGPROC, LPARAM)'|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h||In function 'HFONT__* CreateFont(int, int, int, int, int, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, LPCTSTR)':|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h|69|error: cannot convert 'LPCTSTR {aka const char*}' to 'LPCWSTR {aka const wchar_t*}' for argument '14' to 'HFONT__* CreateFontW(int, int, int, int, int, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, LPCWSTR)'|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h||In function 'HWND__* CreateWindow(LPCTSTR, LPCTSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)':|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h|94|error: cannot convert 'LPCTSTR {aka const char*}' to 'LPCWSTR {aka const wchar_t*}' for argument '2' to 'HWND__* CreateWindowExW(DWORD, LPCWSTR, LPCWSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)'|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h||In function 'HMENU__* LoadMenu(HINSTANCE, LPCTSTR)':|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h|111|error: cannot convert 'LPCTSTR {aka const char*}' to 'LPCWSTR {aka const wchar_t*}' for argument '2' to 'HMENU__* LoadMenuW(HINSTANCE, LPCWSTR)'|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h||In function 'HWND__* FindText(LPFINDREPLACE)':|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h|126|error: cannot convert 'LPFINDREPLACE {aka FINDREPLACEA*}' to 'LPFINDREPLACEW {aka FINDREPLACEW*}' for argument '1' to 'HWND__* FindTextW(LPFINDREPLACEW)'|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h||In function 'HICON__* LoadIcon(HINSTANCE, LPCTSTR)':|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h|311|error: cannot convert 'LPCTSTR {aka const char*}' to 'LPCWSTR {aka const wchar_t*}' for argument '2' to 'HICON__* LoadIconW(HINSTANCE, LPCWSTR)'|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h||In function 'HBITMAP__* LoadBitmap(HINSTANCE, LPCTSTR)':|
C:\wxWidgets-3.0.2\include\wx\msw\winundef.h|324|error: cannot convert 'LPCTSTR {aka const char*}' to 'LPCWSTR {aka const wchar_t*}' for argument '2' to 'HBITMAP__* LoadBitmapW(HINSTANCE, LPCWSTR)'|
||=== Build failed: 7 error(s), 0 warning(s) (0 minute(s), 1 second(s)) ===|


I don't quite understand this behaviour, because the header is a thread and the error comes from the dialog that uses this thread. Can anyone explain why C++ (or wxWidgets) behaves like this?

Edit:

The includes of Serial.h

#ifndef SERIAL_H
#define SERIAL_H

#include <windows.h>
//#include <wx/msw/winundef.h>
#include <stdio.h> // necessary for sprintf
#include <string>


It seems to fit Marco's comment, but I cannot include the part...

VZ. VZ.
Answer

The problem is that <windows.h> defines different symbols depending on whether UNICODE standard (under Windows, that is) macro is defined or not. If you include wxWidgets headers first, which assume Unicode build by default, they define UNICODE for you before including <windows.h> and everything is good.

If you include <windows.h> first, then UNICODE is not defined at this time, but when you include wxWidgets headers later they use wxUSE_UNICODE=1 (which is, again, the default value) resulting in the compilation problems you're observing.

The simplest way to ensure you never have such problems is to define UNICODE globally, in the project settings or makefile.

Comments