VC10 Compiler Porting

Introduction

3ds Max 2013 has been upgraded to use version 10.0 of the Microsoft C/C++ compiler. This is the compiler that comes with Visual Studio 2010 Service Pack 1. Plug-in developers can now use Visual Studio 2010 with Platform Toolset vc100 to compile their projects. This document is a guide for upgrading plugins to compile with Visual Studio Service Pack 1.

Breaking Changes in 3ds Max

With the introduction of VC 10.0 there were few breaking changes in the max code base. These are specific, or mostly specific to the 3ds Max code base. Simple issues such as adding includes will not be discussed here are they are simple to diagnose, and already described on the MSDN.

Renamed ParamTags::end to ParamTags::p_end

Inside of ParamType.h is an enum called ParamTags with an enumerator called end. This conflicts with new symbols in the STL std namespace. For instance,

1> ...\xmlmtlexporter.cpp(233): error C2872: 'end': ambiguous symbol
1>    could be '...\paramtype.h(665): ParamTags end'
1>    or    'end'

The compiler is not going to show what the conflicting symbol is. However this is caused by adding a 'using namespace std' declaration in a header file. It is generally not recommended to add a using namespace declaration in a header file. Also, the symbol name 'end' was too generic, so the enumeration was renamed to avoid potential conflicts here and in the future.

Small block heap no longer supported

The C Runtime (CRT) header file malloc.h no longer declares nor defines the functions _get_sbh_threshold or _set_sbh_threshold. As such, any usage of these functions should be removed.

STL Set Iterators are all const

The C++ Standard Template Library in VC 10.0 no longer supports non-const iterators for sets and hash_sets. Thus, the std::set<>::iterator behaves identically to the const_iterator.

In particular, de-referencing the iterators will now return const types instead of non-const types. For instance, the following code may no longer work for an std::set iterator.

MyType& t = *it;

Instead, you would have to make the type const.

const MyType& t = *it;

You could alternatively perform a copy using the assignment operator, and drop the reference symbol.

MyType t = *it;

To ensure that iterators are not altered within a loop, only const methods can be called on const types. Because set and hash_set is keyed on the type itself, performing non-const operations on the instance is now forbidden.

// Avoid the following for an iterator 'it':
(*it)->changeThis();

// Proper call of a const method:
(*it)->MyConstMethod();

One way to fix this is to make the method declaration const. For instance, the virtual method SelectFilterCallback::IsFiltered in maxapi.h was not const and failed to compile because the filters were stored in a set, and iterated using iterators. Changing this method to const enabled the code to compile properly. Note however that the compiler does not always flag such instances as issues. For instance, the following code compiles properly:

const AnimatableNotifierPtr& pNotifier = (*it);
pNotifier->SetOwnerSet(NULL);

In cases where the iterator will be modified, a useful alternative to std::set<> is std::map<>. For instance, a hash_set can be replaced by a hash_map.

Named Template Parameters

Passing in arguments to functions through the template type instead of the normal function parameter list is no longer supported. The following example illustrates the proper use of templates.

// Wrong: the 'AccumulatorType wholeScale' is included in the template definition.
template <class AccumulatorType, class DataType, AccumulatorType wholeScale>
static inline void scale(const DataType *source, int sourceCount, DataType *dest, int destCount) { 
    //...
}

// Correct: the 'AccumulatorType wholeScale' is a parameter in the scale() function.
template <class AccumulatorType, class DataType>
static inline void scale(const DataType* source, int sourceCount, DataType* dest, int destCount, AccumulatorType wholeScale) {
    // ...
}

.NET 4.0

Previous versions of 3ds Max used a range of .NET 2.0 and .NET 4.0 functionality. 3ds Max 2013 now only uses .NET 4.0.

Possible Compiler Errors

The following is a list of frequently encountered compiler errors encountered while porting 3ds Max to the VC 10.0 compiler.

error C2678 - std::set<>::iterator is now const; it cannot have non-const methods invoked on it.

binary '=' : no operator found which takes a left-hand operand of type 'const <type>' (or there is no acceptable conversion)

Operations such as the following ones are therefore disallowed:

// Will not compile:
MaterialTemplates::iterator p = MaterialTemplates::msTemplates.find(info.mID);
*p = info;

error C2872 or error C2664 - ParamTags::end was renamed to ParamTags::p_end in paramtype.h.

error C2872: 'end' : ambiguous symbol
    could be '<filepath> : ParamTags end'
    or       'end'

error C1189 - The use of #define _WIN32_WINNT 0x0400 targets Windows 95 and below. Note that 3ds Max is not supported on Windows 95, nor Windows 98, nor Windows 2000. Plugin developers should adhere to #define _WIN32_WINNT 0x0502 (MaxWindowsVersion.h in the sdk), corresponding to Windows XP Service Pack 2.

fatal error C1189:
#error : This file requires _WIN32_WINNT to be #defined at least to 0x0403. The value 0x0501 or higher is recommended.

error C2440 - This may occur if you are attempting to pass a NULL or zero (0) value to an STL method or constructor which requires a real pointer. Note that the keyword nullptr has been introduced in VC 10.0 to account for null value instantiations.

error C2440: 'initializing' : cannot convert from 'int' to 'StPathObject *'

For example, an std::map can be defined in the following way:

std::map<int*, int*> mymap;

If you tried to insert NULL into it, the compiler would complain with the warning above.

// Will not compile:
mymap.insert(std::pair<int*, int*>(NULL, NULL));

Similarly, defining a standard pair is also invalid:

// Will not compile:
std::pair<int*, int*>mypair(NULL, NULL);

Both of these approaches can be fixed by passing in properly typed arguments. For instance, you can now pass in nullptr.

// Will compile:
mymap.insert(std::pair<int*, int*>(nullptr, nullptr));

error C2039 - Resolved by using boost version 1.44 or newer.

1> ...\file.hpp(141): error C2039: 'unchecked_copy' : is not a member of 'stdext'

This is documented by Microsoft as a breaking change, where it says "In the <algorithm> header, the checked_* and unchecked_* functions are removed".

Possible Linker Errors

error LNK2019 - A result of linking against libraries which contain STL code compiled with VC 9.0.

error: LNK2019: unresolved external symbol "__declspec(dllimport) public:class std::basic_string<whcar_t,struct std::char_traits<wchar_t>,..."

error LNK2005 - Occurs with DLLs which make extensive use of MFC. Ensure that stdafx.h is included first at the top of every .cpp file in the project. Consult the knowledge base article on Microsoft: http://support.microsoft.com/kb/148652.

error: LNK2005: _DllMain@12 already defined in MSVCRTD.LIB (dllmain.obj)