Saturday, February 06, 2010

Redistributable Documents

This web page hosts redistributable documents. The aim of this web page is to make things more convenient for those who want to access the document off-line or to obtain some software bundles in a convenient way. Documents that are not redistributable are noted and links to the official web sites are provided, instead.

OpenGL 2.1 Reference Pages (Man Pages)

(Feb 2010)These man pages are a slightly modified version provided by the Khronos group. Typically, we have to SVN to get these man pages. The official package of the man pages, however, contain more information than what I need (most people may feel the same). If you want only HTML man pages, you might want to download my package (2MB). There is only one addition, index_frame.html, which is a frame version of the man page we see in this official web site. I hope this helps those who do not want to deal with SVN and want the frame version.

MinGW 32-bit

(May 17, 2010)Since MinGW installer is not finalized yet, it is awkward to get GCC 4.5.0 from the server. Therefore, I gathered all files that are downloaded by mingwdl.sh discussed in MinGW web site and made a package that is ready to use. Just download the package if you don't need anything newer than the April-16-2010 release (GCC 4.5.0). With the package, there is no need to bother with wget or bash.

Thursday, February 04, 2010

Dealing with Build Errors in Visual C++ Project

Warning C4251 for vector type in DLL build

When we build a shared library, it is possible that container specialization type is not available outside the scope of the shared library, as the caller may not know such type.This, however, generates only warning for an STL container. We can deal with this warning by explicit declaration of the library interface.

For example: template class DLL_IMEXPORT_DEF std::vector<int>;

Unfortunately, there may be another warning if the type in the STL container is a class or a pointer to an object, not a primitive type.For example: template class DLL_IMEXPORT_DEF std::vector<XmlNode>;causes a warning about std::allocator<_Ty,_Alloc>.

This problem can be solved (sort of, see the note below) by adding a dll interface of the problematic allocator in front of the export of the intended STL container.That is, we have to write (order of declaration is important).template class XML_MANAGER_IMEXPORT std::allocator<XmlNode>;template class XML_MANAGER_IMEXPORT std::vector<XmlNode>;to be free of the warning.

A vector of CString has some special requirements for its DLL interface. I need to have additional interface declaration, as shown below.template class XML_MANAGER_IMEXPORT std::allocator<CString>;template class XML_MANAGER_IMEXPORT std::_Container_base_aux_alloc_real< std::allocator<CString> >;template class XML_MANAGER_IMEXPORT std::vector<CString>;

Note: exporting STL template specialized class may cause link error with multiply defined class. Microsoft discusses about a way to deal with STL export in this link

More info is available at:http://www.unknownroad.com/rtfm/VisualStudio/warningC4251.html

Polymorphism with Boost shared_ptr

Using a shared_ptr is a great way to avoid almost all kinds of memory leak. Moreover, we have no need to find out the last owner of an object. Therefore, complexity in programming logic is reduced. Unfortunately, it is not straightforward to employ polymorphism with a shared_ptr, as we cannot just cast the pointer in a typical C++ fashion, e.g. dynamic_cast. Thus, Boost library comes with a special way to cast its smart pointers.

If you deal with the polymorphism in a vector. For example, we want to have std::vector< boost::shared_ptr<T_Base> > to hold shared_ptr of T_Child1 and T_Child2. In this example, we can do it as follows.

There is nothing to worry about calling virtual functions. They will be called properly as usual.

Definition of a template class in a DLL cannot be found

If a template class is not employed within the DLL itself, its actual binary may not be created at a compile time or link time.Also, if we inline everything in the template class and use the class inside the DLL, its actual binary may not be created and accessible by other DLLs or applications.We can deal with the problem by specialization of the template class and avoiding inlining constructors and destructors of the class. Performances may decrease, but it solves the problem.

Cause:"The CRT libraries use weak external linkage for the new, delete, and DllMain functions. The MFC libraries also contain new, delete, and DllMain functions. These functions require the MFC libraries to be linked before the CRT library is linked." [Reference]

This can be fixed by forcing the order of library linking. This is shown in the above reference and directly quoted here:

"Visual C++ does not contain this header file. To create this file, follow these steps:

Open Msdev\Mfc\Include\Afx.h.

Select the lines between #ifndef _AFX_NOFORCE_LIBS and #endif //!_AFX_NOFORCE_LIBS.

Copy the selection to the Windows Clipboard.

Create a new text file.

Paste the contents of the Clipboard into this new file.

Save the file as Msdev\Mfc\Include\Forcelib.h."

Separate inline function declaration and definition

In Visucal C++, separating an inline function declaration and definition across header and source files is not possible, unless the inline function is virtual. However, we can separate declaration and definition in the same header file. The only difference is that we should add a keyword inline in front of the definition. Example:

"When you declare an inline member function, it looks just like a normal member function:

class Fred { public: void f(int i, char c); };

But when you define an inline member function, you prepend the member function's definition with the keyword inline, and you put the definition into a header file:

inline void Fred::f(int i, char c) {... }

It's usually imperative that the function's definition (the part between the {...}) be placed in a header file. If you put the inline function's definition into a .cpp file, and if it is called from some other .cpp file, you'll get an "unresolved external" error from the linker." [This is quoted from another web site.]

Building and Debugging Tips in Visual C++

(Jan 2010-Present)

This document compile my ongoing experience with Visual C++. There are many more tips I know, but they are related to generic C++. I'm thinking about creating a separate document for them or just put them here.

Can we debug a DLL project? Why can we set a DLL project as a startup project, even though it is not executable per se?

Yes, we can debug a DLL project. Plus, it does make sense to make it a startup project. In fact, setting it as a startup project is a way to debug the DLL project, provided that we have any executable file to call the DLL.

Complete info is a available in this link. Here, I quoted the essential information for Visual C++.

"

To specify the calling application in a C++ project

In Solution Explorer, select the DLL project.

On the View menu, choose Property Pages.

In the Project Property Pages window, in the Configuration drop-down list, choose Debug.

Open the Configuration Properties folder, and select the Debugging category.

In the Debugger to launch list, choose Local Windows Debugger or Remote Windows Debugger.

In the Command or Remote Command box, click the drop-down arrow, and select Browse from the list to locate the application. Alternatively, type the path and name of the application.

Type any necessary program arguments in the Command Arguments box.

"

What if a break point cannot be hit because 'no symbol has been loaded for this document'.

In Visual C++, this seems to be a common problem. First of all, we have to check if we have debugging symbols loaded or not. Go to the 'Output' window (this is typically one of the tabs at the bottom of your VC++ window). Then, look for a line telling about loading DLL. For example, there should be a line like this

'TesterConsole.exe': Loaded 'F:\Work\A.dll', [IMPORTANT MESSAGE HERE]

Next, check what your IMPORTANT MESSAGE HERE is. If it is 'Binary was not built with debug information', we have to check a few points in our project properties. Go to the project properties. In the tab 'Configuration Properties -> C/C++ -> General', it is best that 'Debug Information Format' is a good one, which is 'Program Database (/Zi)' and 'Program Database for Edit & Continue (/ZI)'. Then, go to a neigboring tab 'Optimization', it is highly recommended to set 'Optimization' to 'Disabled (/Od)'.

The final step is check your linker option. Go to the tab 'Configuration Properties -> Linker -> Debugging'. Next, set 'Generate Debug Info' to 'Yes (/DEBUG)'.(Some additional information is in this link.)

If your IMPORTANT MESSAGE HERE is NOT about 'Binary was not built with debug information', chances are your debugging information is not synchronized with your DLL or your PDB (program data base) file is corrupt. Try cleaning and rebuilding the affected DLL project.

Stand Between Debug and Release Modes

In some cases, it is too time consuming to debug a program in the Debug mode for a computationally intensive application, especially if we only want to see a crash site and call stack. In such cases, we can choose to create another configuration. Let's call the configuration 'Release w Assertion Enabled'. This is actually a release mode without a preprocessor symbol NDEBUG. Without NDEBUG, all assertion mechanisms are performed like in a debug mode, but the code is optimized. If your program crashes, you can see the crash site and call stack, even though you cannot see accurate values of variables. This is significantly useful when we need to get some idea about the problem.

At this point, you might ask, 'Why do we need another configuration?' and 'Why don't we just remove NDEBUG during debugging process and insert it back when debugging is done?' The answer is if we change any preprocessor symbol, the compiler must re-compile everything. This is very time consuming if your project is large. Moreover, in most cases, the debugging process never ends. We have to go back and forth between release and debug builds all the time. Creating a separate build, therefore, is a better way for a large and computationally intensive project in the long run.

Playing around with Visual C++, I found that Visual C++ might behave strangely if I modify a debug mode by just removing the preprocessor symbol _DEBUG and letting the executable call another library built in a release mode. In the scenario, the run-time check might find that the heap or stack around a variable from the library is corrupted, but there is actually nothing wrong.

String Conversion

String conversion is usually necessary when we work with XML data, as XML uses Unicode character set by default. Most parts of our work, however, may mainly use the ANSI character set. This typically requires us to employ CA2W to convert an ANSI text to a Unicode text. The usage of CA2W, however, may be quite unnatural to most of us.

For example, the use LPCWSTR szr = CA2W(szReplaceFile); is actually invalid because LPCTSTR becomes a pointer to a temporary string created during a conversion. Therefore, it will be scrapped immediately after CA2W returns. The correct use is CA2W szr(szReplaceFile);. Look strange, right? No doubt, a macro is usually strange.

Assertion Failed at the line ASSERT(AfxGetThread() == NULL)

This assertion is at the line 380 in appcore.cpp of Visual C++ 2008--version 9.0. (Note that other versions may have this assertion at different lines.) This issue arises if there are multiple MFC-application threads running at the same time (to be more specific, they are generated by CWinApp objects). It is usually caused by calling MFC DLLs that comes with their own 'theApp', an object derived from CWinApp. When these DLLs are initialized, they may start their own MFC-application threads, while we expect to have only one MFC-application thread--the one from our executable project.

There is a discussion about this issue in this link. Unfortunately, it does not tell us much about any practical approach for a solution. Thus, we have to figure this out ourselves. If your application involves many DLLs, you probably have no idea what DLLs are culprits. After trying for hours, I finally come up with a systematic method to identify the problematic DLLs and, of course, I have a solution for this problem.

To begin, set a break point at the assertion line ASSERT(AfxGetThread() == NULL). Then, start the debugger. At the break point, check 'Call Stack'. (Those who are not familiar with debugging and the call stack might want to see this reference.) If the call stack involves a DLL that is not mfcXXd.dll, such a DLL is likely to be a culprit, especially if it uses MFC in a shared library.

Next, check the source code of the library and look for the file with 'theApp' in the suspect library. Then, check its InitInstance() function. If you find no AFX_MANAGE_STATE(AfxGetStaticModuleState( )); at the beginning of InitInstance(), insert it at the very beginning of the function. Then, continue this process until every involved CWinApp DLL is properly equipped with AFX_MANAGE_STATE(AfxGetStaticModuleState( )).

If the AFX_MANAGE_STATE causes linker errors, you might have an invalid preprocessor symbol. The CWinApp DLL is usually configured with _USRDLL, not _LIB, because it is a dynamically linked library. You may find that changing _LIB to _USRDLL helps solve your linker errors.

Halt at First-Chance Exceptions

First-chance exceptions in Visual C++ are a kind of access violation with code c0000005. Many of them are not critical and we can possibly ignore them. However, it is better, if we can get rid all of them, as your customers may demand that no compiler/linker warnings and no such exceptions are present when you ship your product.

Visual C++, nonetheless, may not stop at the problematic point and just continue. This makes it hard to tackle the problem. So, we might want to force break for all exception. The exception sheet can be accessed from menu Debug -> Exceptions (also by Ctrl-Alt-E, see Figure FCE1) in Visual Studio 2008.

If all exceptions are to be tackled, check all of the check boxes there so that all exception will be thrown (and we are forced to handle or get rid of them from the first place). See Figure FCE2. If only first chance exceptions, which are in fact a kind of access violtion, are to be handled, go to Win32 Exceptions and check Access Violation (Figure FCE3).

Note that there is a button on the right that can reset exception settings to default and can even add a new one to the list (Figure FCE 4). Adding a new one can possibly add our custom exceptions to the list, I belive (not try it myself yet).

Figure FCE1. Exeption setting menu

Figure FCE2. Set to throw all exceptions known by Visual C++.

Figure FCE3. Focus on first-chance exception. However, it is recommended to (finally) handle all kinds of exceptions that come into your way.

Figure FCE4. Remember that we can reset the settings or add a new exception to the list.

Program Exits with Code 0, but Unexpectedly

Typically, a program exiting with code 0 means there is nothing wrong. However, sometimes a program exits in this way unexpectedly. Also, nothing shows up at all. I encountered this problem when I tried to build a program that uses a statically linked library. That library, nonetheless, was created as a dynamically linked library before. Therefore, there is a variable theApp inside its main file.

In general, this does not cause any problem and we can switch between dynamic-lib and static-lib configurations any time we want. Nevertheless, there is one thing we need to be careful. In a static-lib configuration, we are not supposed to let the program execution see theApp. Why? Because theApp will be initialized and start an instance of an MFC application thread. Subsequently, theApp just exits with code 0. This problem arises if we add something to the library main file and that file is in a part of compilation/execution.

To check if that is the actual cause of the problem, set a break point at the beginning of InitInstance() in a suspect library. If the function is called, theApp is initialized in a wrong way. Therefore, we need to find a way to prevent theApp to get involved. This can be done by removing things that you add to the library main file. If that means a lot of work, try using the macro to exclude theApp.

For example:#ifdef _NO_THEAPPCLibApp theApp;#endif

The above lines of code should effectively deal with the problem, provided that _NO_THEAPP is defined properly (as a preprocessor, for example).