Thursday, February 04, 2010

Dealing with Build Errors in Visual C++ Project

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.

boost::shared_ptr<T_A> testPtr(new T_A(blah));

boost::shared_ptr<T_B> anotherTestPtr = boost::dynamic_pointer_cast<
T_B>(testPtr));

That is 'boost::dynamic_pointer_cast' is a way to solve this issue.

More info can be found here and there.

====================

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.

std::vector< boost::shared_ptr<T_Base> > vecPoly;
boost::shared_ptr<T_Base> childPtr1( new T_Child1() );
boost::shared_ptr<T_Base> childPtr2( new T_Child2() );

vecPoly.push_back( childPtr1 );
vecPoly.push_back( childPtr2 );

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.

Error with afximpl.h

When a project incude afximpl.h (typically in stdafx.h), compile errors may show up in Visual Studio 2008 (Visual C++ 9.0).  The error message looks like:
1>c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\..\atlmfc\src\mfc\afximpl.h(635) : error C2059: syntax error : '<L_TYPE_raw>'
1>c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\..\atlmfc\src\mfc\afximpl.h(635) : error C2238: unexpected token(s) preceding ';'

It seems that Microsoft did not purge this error yet, but there is a workaround that is effective.

"place the following line in your code before including afximpl.h:
DECLARE_HANDLE(HRAWINPUT); "

Reference: http://connect.microsoft.com/VisualStudio/feedback/details/276404/windows-2000-settings-incompatible-with-afximpl-h#details

error LNK2005: "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z) already defined in ...

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:
  1. Open Msdev\Mfc\Include\Afx.h.
  2. Select the lines between #ifndef _AFX_NOFORCE_LIBS and #endif //!_AFX_NOFORCE_LIBS.
  3. Copy the selection to the Windows Clipboard.
  4. Create a new text file.
  5. Paste the contents of the Clipboard into this new file.
  6. 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.]




------------------------------------
Pinyo Taeprasartsit

(This document can be viewed at my blog and Google docs)


No comments: