📜 ⬆️ ⬇️

Project C ++ Migration from Visual Studio 2008 to Visual Studio 2010

The adoption of the new C ++ standard is not far off, and (fortunately?) Compiler developers give programmers the opportunity to try out some of the new features that appear in the language now. In particular, the latest version of Visual Studio supports a number of innovations, which have already been written more than once or twice. But one thing is to demonstrate the possibilities on synthetic examples, another is to try them in combat mode (obviously, at your own peril and risk). This article is the result of such an experiment. So.

Source material: game project with source code in C ++. Boost (1.40), Qt (4.x) user interface is actively used.
Task: to translate the project to Visual Studio 2010 for further development using this compiler and its new features (in the long run, gcc 4.5 and later transitions are possible).

In the process of transferring the code, not only compilation errors that came out during assembly with the new compiler were corrected, but cosmetic reworking of the code was performed, i.e. replacing a number of constructions used from boost with similar ones from STL / language (deeper processing of the code will be done later) . Below are some of the problems encountered, and how to solve them. Yes, after making the changes and the final recompilation, everything started and worked.
')

Null vs nullptr

The problem came out when building Qt (4.6.3) on code, like the following:
std ::pair<SomeStruct*, SomeStruct*> pair( NULL , NULL );

Such code is perfectly built for VS 2005/2008, but does not work for VS 2010. For correct assembly, you must replace NULL with nullptr:
std ::pair<SomeStruct*, SomeStruct*> pair( nullptr , nullptr );

The problem is that the std :: pair has a template constructor:
template < class _Other1,
class _Other2>
pair(_Other1&& _Val1, _Other2&& _Val2)

Which was not in previous versions of STL.

boost :: bind vs std :: bind

Unfortunately, the current implementation (and specification) of std :: bind is in many ways less functional, its counterpart from boost. In particular:
1. There are problems with compiling this kind of code:

struct SomeStruct
{
int m_dataMember;
int GetValue() const { return m_dataMember;}
};

void PrintValue( int val) { /* ... */ }

std ::vector<SomeStruct> vec;

std ::for_each(vec.begin(), vec.end(), std ::bind(PrintValue, std ::bind(&SomeStruct::m_DataMember, std ::placeholders::_ 1 )));


In this case, this option:
std ::transform(vec.begin(), vec.end(), std ::ostream_iterator( std :: cout , ' ' ), std ::bind(&SomeStruct::m_DataMember, std ::placeholders::_ 1 ));

or
std ::for_each(vec.begin(), vec.end(), std ::bind(PrintValue, std ::bind(&SomeStruct::GetValue(), std ::placeholders::_ 1 )));

compiles successfully. A number of experiments have shown that there are problems in the implementation of std :: bind when matching reference and non-reference types in the chain of binding.

2. For binders, arithmetic and logical operations are not overloaded, so the “classic”:
auto res = std ::find_if(vec.begin(), vec.end(), std ::bind(&SomeStruct::m_DataMember, std ::placeholders::_ 1 ) == 1 );


not acceptable.

3. Placers are moved from global namespace to std :: placeholders, which (for ease of use) forces you to enter an alias of the form:
namespace ph = std ::placeholders;

bind vs lambda

Lambda functions (due to their syntax) are somewhat more “verbose” than “good old” binders. The previous search example in a container can be rewritten as:
auto res = std ::find_if(vec.begin(), vec.end(), [](SomeStruct const & s)
{
return s.m_Value == 1 ;
});


either (same, but in one line)
auto res = std ::find_if(vec.begin(), vec.end(), [](SomeStruct const & s) { return s.m_DataMember == 1 ;});

for comparison:
auto res = std ::find_if(vec.begin(), vec.end(), boost::bind(&SomeStruct::m_DataMember, _1 ) == 1 );

and which of the options to give preference - you need to decide in each case separately.

Simplify BOOST_FOREACH

The implementation of BOOST_FOREACH is quite heavy and cumbersome. And (in my opinion) in most cases can be facilitated in at least two ways. Method number one is a simplified implementation of the macro BOOST_FOREACH. For example, in this way:
#define FOREACH(Var, cont) \
if ( bool do_continue_ = true) \
for ( auto foreach_iter = std ::begin(cont); foreach_iter != std ::end(cont); ++ foreach_iter, do_continue_ = true) \
for (Var = *foreach_iter; do_continue_; do_continue_ = false)


Method number two is to replace std :: for_each + lambda with a bunch:
std ::for_each( std ::begin(vec), std ::end(vec), [](TestClass const & val)
{
std :: cout << val.m_Value << ' ' ;
});

Which option is preferable - to decide, again, it is necessary in place. I have done a reimplementation of the macro so far (so that the code is less edited). Also, do not forget that for access to local variables from the lambda function requires their enumeration in the capture-list.

vector <charT> vs basic_string <charT>

By virtue of the requirements of the new standard, store the contents of the string sequentially (21.4.1, paragraph 5), from similar structures:
std ::ifstream fs( /*...*/ );

// ...

std ::vector< char > buff(some_size);
fs.read(&buff[ 0 ], some_size);
return std :: string (buff.begin(), buff.end());

You can exclude an intermediate buffer in the form of a vector:
std :: string ret_val(some_size, '\0' );
fs.read(&ret_val[ 0 ], some_size);
return ret_val;

Little things

- BOOST_AUTO makes sense to replace with auto;
- BOOST_TYPEOF to decltype;
- cont.begin () / cont.end () to the more generalized std :: begin (cont) / std :: end (cont).

Source: https://habr.com/ru/post/98018/


All Articles