摘要: My name is Ahmed Charles and I currently work on Windows Error Reporting. I believe that this is the first time that someone not on the VC team has written a blog, but I hope you will find it useful a...
阅读全文
摘要: The Visual C++ compiler in the Microsoft Visual Studio 2010 September Community Technology Preview (CTP) contains support for four C++0x language features, namely lambdas, auto, static_assert, and rva...
阅读全文
摘要: This entry is part of a series, Expressive C++» Entries in this series:
Expressive C++: Introduction
Expressive C++: Playing with Syntax
Expressive C++: Why Template Errors Suck ...
阅读全文
C:\Temp>type meow.cpp
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
class Foo {
public:
explicit Foo(const int x) : m_x(x) { }
void mult(vector<vector<int>>& outer) const {
for_each(outer.begin(), outer.end(), [&](vector<int>& inner) {
for_each(inner.begin(), inner.end(), [&](int& elem) {
elem *= m_x;
});
});
}
private:
int m_x;
};
int main() {
Foo f(1000);
vector<vector<int>> v(3, vector<int>(4));
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
v[i][j] = i * 10 + j;
}
}
f.mult(v);
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
cout << v[i][j] << " ";
}
cout << endl;
}
}
C:\Temp>cl
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 17.00.40309 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
usage: cl [ option... ] filename... [ /link linkoption... ]
C:\Temp>cl /EHsc /nologo /W4 meow.cpp
meow.cpp
meow.cpp(14) : error C3494: 'this' cannot be captured in a nested lambda
meow.cpp(14) : error C2065: 'm_x' : undeclared identifier
meow.cpp(14) : error C2065: 'm_x' : undeclared identifier
C:\Temp>g++ -Wall -Wextra -std=c++0x meow.cpp -o meow.exe
C:\Temp>meow
0 1000 2000 3000
10000 11000 12000 13000
20000 21000 22000 23000
There's a workaround:
C:\Temp>type meow.cpp
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
class Foo {
public:
explicit Foo(const int x) : m_x(x) { }
void mult(vector<vector<int>>& outer) const {
const Foo * const myself = this;
for_each(outer.begin(), outer.end(), [&](vector<int>& inner) {
for_each(inner.begin(), inner.end(), [&](int& elem) {
elem *= myself->m_x;
});
});
}
private:
int m_x;
};
int main() {
Foo f(1000);
vector<vector<int>> v(3, vector<int>(4));
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
v[i][j] = i * 10 + j;
}
}
f.mult(v);
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
cout << v[i][j] << " ";
}
cout << endl;
}
}
C:\Temp>cl /EHsc /nologo /W4 meow.cpp
meow.cpp
C:\Temp>meow
0 1000 2000 3000
10000 11000 12000 13000
20000 21000 22000 23000
C:\Temp>g++ -Wall -Wextra -std=c++0x meow.cpp -o meow.exe
C:\Temp>meow
0 1000 2000 3000
10000 11000 12000 13000
20000 21000 22000 23000
C:\Temp>type meow.cpp
#include <type_traits>
template <typename T> struct A {
A() {
static_assert(std::is_base_of<A<T>, T>::value, "assert1");
typedef typename std::remove_reference<decltype(*this)>::type meow_t;
static_assert(std::is_base_of<meow_t, T>::value, "assert2");
}
};
struct C : public A<C> { };
void foo() {
C c;
}
C:\Temp>cl
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 17.00.40316 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
usage: cl [ option... ] filename... [ /link linkoption... ]
C:\Temp>cl /EHsc /nologo /W4 /c meow.cpp
meow.cpp
C:\Temp>
*this is an lvalue so decltype(*this) is indeed A<T>&.
from MSDN:
The volatile keyword is a type qualifier used to declare that an object can be modified in the program by something other than statements, such as the operating system, the hardware, or a concurrently executing thread.
The following example declares a volatile integer nVint whose value can be modified by external processes:
int volatile nVint;
Objects declared as volatile are not used in optimizations because their value can change at any time. The system always reads the current value of a volatile object at the point it is requested, even if the previous instruction asked for a value from the same object. Also, the value of the object is written immediately on assignment.
One use of the volatile qualifier is to provide access to memory locations used by asynchronous processes such as interrupt handlers.
This means a value that can automatically change at any given instance. This is usually in reference to a hardware register that often has a different value ever time you read it. Specifically, it is useful to define some values as volatile so that a C compiler will not cache that value, but rather, get a fresh copy every time you request it's value. Otherwise, the compiler may return values which are inaccurate because it doesn't realized the value can change.
C:\tmp>type honor_volatile.c
#if !defined(VOLATILE)
#error
#endif
void test_one()
{
VOLATILE int x = 5;
}
void test_two(VOLATILE int x)
{
x = 5;
}
C:\tmp>cl -O2 -c -Fa -DVOLATILE= honor_volatile.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 17.00.40308 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
honor_volatile.c
C:\tmp>type honor_volatile.asm
; Listing generated by Microsoft (R) Optimizing Compiler Version 17.00.40308.0
TITLE C:\tmp\honor_volatile.c
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
PUBLIC _test_one
PUBLIC _test_two
; Function compile flags: /Ogtpy
; COMDAT _test_two
_TEXT SEGMENT
_x$ = 8 ; size = 4
_test_two PROC ; COMDAT
; File c:\tmp\honor_volatile.c
; Line 15
ret 0
_test_two ENDP
_TEXT ENDS
; Function compile flags: /Ogtpy
; COMDAT _test_one
_TEXT SEGMENT
_test_one PROC ; COMDAT
; File c:\tmp\honor_volatile.c
; Line 10
ret 0
_test_one ENDP
_TEXT ENDS
END
C:\tmp>cl -O2 -c -Fa -DVOLATILE=volatile honor_volatile.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 17.00.40308 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
honor_volatile.c
C:\tmp>type honor_volatile.asm
; Listing generated by Microsoft (R) Optimizing Compiler Version 17.00.40308.0
TITLE C:\tmp\honor_volatile.c
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
PUBLIC _test_one
PUBLIC _test_two
; Function compile flags: /Ogtpy
; COMDAT _test_two
_TEXT SEGMENT
_x$ = 8 ; size = 4
_test_two PROC ; COMDAT
; File c:\tmp\honor_volatile.c
; Line 14
mov DWORD PTR _x$[esp-4], 5
; Line 15
ret 0
_test_two ENDP
_TEXT ENDS
; Function compile flags: /Ogtpy
; COMDAT _test_one
_TEXT SEGMENT
_x$ = -4 ; size = 4
_test_one PROC ; COMDAT
; File c:\tmp\honor_volatile.c
; Line 8
push ecx
; Line 9
mov DWORD PTR _x$[esp+4], 5
; Line 10
pop ecx
ret 0
_test_one ENDP
_TEXT ENDS
END
And this is assembly code from VS:
#include <stdio.h>
void main()
{
volatile int i=10;
int a = i;
printf("i= %d\n",a);
__asm {
mov dword ptr [ebp-4], 20h
}
int b = i;
printf("i= %d\n",b);
}
Release:
009C1002 in al,dx
009C1003 push ecx
009C1004 push esi
volatile int i=10;
int a = i;
printf("i= %d\n",a);
009C1005 mov esi,dword ptr [__imp__printf (9C20A0h)]
009C100B mov dword ptr [i],0Ah
009C1012 mov eax,dword ptr [i]
009C1015 push eax
009C1016 push offset string "i= %d\n" (9C20F4h)
009C101B call esi
009C101D add esp,8
__asm {
mov dword ptr [ebp-4], 20h
009C1020 mov dword ptr [i],20h
}
int b = i;
009C1027 mov eax,dword ptr [i]
printf("i= %d\n",b);
009C102A push eax
009C102B push offset string "i= %d\n" (9C20F4h)
009C1030 call esi
009C1032 add esp,8
009C1035 pop esi
}
009C1036 mov esp,ebp
009C1038 pop ebp
009C1039 ret
Debug:
#include <stdio.h>
void main()
{
001B13B0 push ebp
001B13B1 mov ebp,esp
001B13B3 sub esp,0E4h
001B13B9 push ebx
001B13BA push esi
001B13BB push edi
001B13BC lea edi,[ebp-0E4h]
001B13C2 mov ecx,39h
001B13C7 mov eax,0CCCCCCCCh
001B13CC rep stos dword ptr es:[edi]
volatile int i=10;
001B13CE mov dword ptr [i],0Ah
int a = i;
001B13D5 mov eax,dword ptr [i]
001B13D8 mov dword ptr [a],eax
printf("i= %d\n",a);
001B13DB mov esi,esp
001B13DD mov eax,dword ptr [a]
001B13E0 push eax
001B13E1 push offset string "i= %d\n" (1B573Ch)
001B13E6 call dword ptr [__imp__printf (1B82D4h)]
001B13EC add esp,8
001B13EF cmp esi,esp
001B13F1 call @ILT+300(__RTC_CheckEsp) (1B1131h)
__asm {
mov dword ptr [ebp-4], 20h
001B13F6 mov dword ptr [ebp-4],20h
}
int b = i;
001B13FD mov eax,dword ptr [i]
001B1400 mov dword ptr [b],eax
printf("i= %d\n",b);
001B1403 mov esi,esp
001B1405 mov eax,dword ptr [b]
001B1408 push eax
001B1409 push offset string "i= %d\n" (1B573Ch)
001B140E call dword ptr [__imp__printf (1B82D4h)]
001B1414 add esp,8
001B1417 cmp esi,esp
001B1419 call @ILT+300(__RTC_CheckEsp) (1B1131h)
}
C:\Temp>type meow.cpp
#include <stdio.h>
int main() {
MEOW int i = 10;
int a = i;
printf("a: %d\n", a);
__asm {
mov dword ptr [ebp - 4], 20h
}
int b = i;
printf("b: %d\n", b);
}
C:\Temp>cl /EHsc /nologo /W4 /MTd /DMEOW= meow.cpp && meow
meow.cpp
a: 10
b: 32
C:\Temp>cl /EHsc /nologo /W4 /MT /O2 /DMEOW= meow.cpp && meow
meow.cpp
a: 10
b: 10
C:\Temp>cl /EHsc /nologo /W4 /MTd /DMEOW=volatile meow.cpp && meow
meow.cpp
a: 10
b: 32
C:\Temp>cl /EHsc /nologo /W4 /MT /O2 /DMEOW=volatile meow.cpp && meow
meow.cpp
a: 10
b: 32
Volatility inhibits optimizations, as expected.
以const 修饰的常量值,具有不可变性。
C++的编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量。
const 用于指针的两种情况分析:
int const *a; //a/可变,*a不可变
int *const a; //a不可变,*a可变
比如:
typedef char* pStr;
char string[4] = "abc";
//限定*p1不可变,当然p1可变。
const char* p1 = string;
const pStr p2 = string;
p1++; //可行 输出bc
p2++; //不可行
const int nValue; //nValue 是const
const char* pContent; //*pContent是const, pContent可变
char* const pContent; //pContent是const, *pContent可变
const char* const pContent; //pContent和*pContent都是const
#include <iostream>
#include <string>
using namespace std;
string test[] ={"test"};
class StringStack
{
static const int size =100;
const string* stack[size];
int index;
public:
StringStack();
//限定参数在函数体中不可被改变
void push(const string* s);
const string* pop();
//尝试去更改
const string* change(string* s,const string* ss);
};
const string* StringStack::change(string* s,const string* ss)
{
ss=s;
return ss;
}
StringStack::StringStack():index(0)
{
memset(stack,0,size * sizeof(string*));
}
void StringStack::push(const string* s)
{
if(index<size)
{
//改变传进来的常量
s=change(test,s);
//在汇编中可以看出:
0x0137E1C0 68 52 1d 00 63 72 65 61 6d 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 0f 00 00 00 00 00 00 00 b0 52 1d 00 72 69 70 70 hR..cream.......................?R..ripp
0x0137E1E8 6c 65 00 00 00 00 00 00 00 00 00 00 06 00 00 00 0f 00 00 00 00 00 00 00 f8 52 1d 00 6c 65 6d 6f 6e 00 00 00 00 00 00 00 le......................?R..lemon.......
0x0137E210 00 00 00 00 05 00 00 00 0f 00 00 00 00 00 00 00 40 53 1d 00 73 6f 72 62 65 74 00 00 00 00 00 00 00 00 00 00 06 00 00 00 ................@S..sorbet..............
0x0137E238 0f 00 00 00 00 00 00 00 88 53 1d 00 72 6f 63 6b 79 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 0f 00 00 00 00 00 00 00 ........?S..rocky.......................
0x0137E260 d0 53 1d 00 66 75 64 67 65 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 ?S..fudge...............................
0x0137E288 01 00 00 00 d8 22 1d 00 88 51 1d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ....?"..?Q..............................
0x0137E2B0 00 00 00 00 00 00 00 00 00 4c cc 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .........L?.............................
stack[index++]=s;
}
}
//限定函数的返回值不可被更新。
const string* StringStack::pop()
{
if(index>0)
{
const string* rv =stack[--index];
stack[index]=0;
return rv;
}
return 0;
}
string iceCream[]=
{
"cream","ripple","lemon","sorbet","rocky","fudge",
};
const int iCsz=sizeof iceCream / sizeof *iceCream;
int main()
{
StringStack ss;
for(int i=0;i<iCsz;i++)
{
ss.push(&iceCream[i]);
}
const string* cp;
while((cp=ss.pop())!=0)
// ss已经指向了test;所以输出的都是test;
cout<<*cp<<endl;
}
orignal reference: http://blogs.msdn.com/b/jsocha/archive/2011/03/02/c-cli-to-c-tips-and-tricks.aspx
John Socha-Leialoha - MSFT
In my previous post, Writing Unit Tests in Visual Studio for Native C++, I described the approach I’m using to write unit tests for native C++ code by using C++/CLI, which is C++ code that runs inside the .NET runtime. Because of this “mixed” programming model, there are some techniques you may need to employ between C++ and C++/CLI code.
Class Instance Variables
The instance variables in C++/CLI classes are different from instance variables in naïve C++, which means you can’t mix the two blindly. For example, you can’t use native C++ classes as instance variables in your test class. The following, for example, will not compile:
public ref class MyFixture
{
MyClass m_instance;
};
You’ll get the following error message:
error C4368: cannot define 'm_instance' as a member of managed 'Tests::MyFixture': mixed types are not supported
So how do you deal with this? Instead of using a class as the type, you have to use pointers and then create/destroy the instance in the TestInitialize and TestCleanup code:
public ref class MyFixture
{
[TestInitialize]
void Initialize()
{
m_pInstance = new MyClass();
}
[TestCleanup]
void Cleanup()
{
delete m_pInstance;
}
MyClass *m_pInstance;
};
The methods marked by TestInitialize and TestCleanup run before and after each test method.
Passing a Pointer to an Instance Variable
C++/CLI instance variables have a type of interior_ptr<T> instead of the type you wrote in your code. This makes a difference if you attempt to pass either the address or a reference to this instance variable to a native C++ method or function. For example, given the class above, you might think you could write a call to one of your native methods like this:
p->SomeMethod(&m_pInstance);
Compile this and you’ll see this message:
error C2664: 'Tests::MyFixture::MyTest' : cannot convert parameter 2 from 'cli::interior_ptr<Type>' to 'MyClass **'
This error appears because .NET uses a heap—items on the heap can be moved as a result of garbage collection. In order to send a pointer to a native method/function, you need to “pin” the pointer for the duration of the call, which you can do like this:
cli::pin_ptr<MyClass *> p = &m_pInstance;
SomeMethod(static_cast<MyClass **>(p));
Once the variable p either goes out of scope, or is assigned a new value, the pointer will be free to change again.
When you’re dealing with helper methods in your test code, you can also write this type of code:
Helper(&m_pInstance);
...
void Helper(cli::interior_ptr<MyClass *> ppInstance)
{
**ppInstance = new MyClass();
}
function<void()> task = bind( &File::HashFile, static_cast<HashFn>(bind(&Comparator::CompareData, ref(compare), _1, _2, _3, false)), ref(*pFile1) );
function<void()> task = [&](){ File::HashFile( bind(&Comparator::CompareData, ref(compare), _1, _2, _3, false), *pFile1 ); };
function<void()> task = [&](){ File::HashFile([&](const void *pv, size_t cb, const char *szDesc) { compare.CompareData(pv, cb, szDesc, false); }, *pFile1 ); };
(Random comment: The 170 lines of incomprehensible errors you get if you forget the static_cast<HashFn> on the first line is completely obnoxious. [HashFn is a typedef for function<void(const void *, size_t, const char *)>])
The last one is the best
Lambdas supersede bind() 99% of the time - they are easier to read, easier to write, more efficient, and don't explode horribly. The remaining 1% of cases are better handled by handwritten functors (e.g. with templated function call operators).
This is especially true now that 16.1/VC11's lambdas v1.1 handle nested lambdas just fine. (In contrast, nested bind() is a bug farm, which has been tormenting me for years.)
I wish it was true…
Lambdas aren’t polymorphic (and even when I write my own ad hoc function objects I prefer to make them stateless and just use bind for state)
You do not have to specify the argument(s) type(s)
IMHO, for simple bindings, bind is easier to read
Bind has been around for so many years that, at least to me, it just come natural.
I completely agree about nested binds: understanding them is mindbending.
MSDN has conflicting information about nullptr/__nullptr and /clr:
http://msdn.microsoft.com/en-us/library/dd465215(VS.100).aspx says:
The Visual C++ compiler lets you use the nullptr keyword with native code or with managed code. ... The compiler interprets nullptr to be managed code when you use the /clr compiler option, and native code when you do not use the /clr option.
Then adds:
The Microsoft-specific __nullptr keyword has the same meaning as nullptr, but it applies to native code only. If you compile native C/C++ code by using the /clr compiler option, the compiler cannot determine whether the nullptr keyword is a native or a managed term. To make your intention clear to the compiler, use the nullptr keyword to specify the managed term, and __nullptr to specify the native term.
Highlighted part indicates that compiler cannot determine whether it is native or managed, but the previous paragraph just said it will interpret it as managed.
http://msdn.microsoft.com/en-us/library/4ex65770.aspx says the same "cannot determine" thing and then gives an example to contrary:
The following sample shows that nullptr can be assigned to a native pointer when you compile with /clr.
// mcpp_nullptr_6.cpp
// compile with: /clr
int main() {
int * i = 0;
int * j = nullptr;
}
So what should I expect from all of this? I am guessing that the compiler takes nullptr as managed with /clr,
because otherwise there wouldn't be a way to say nullptr in managed, and the sample is just an old garbage?
Here's how it's supposed to work, and what MSDN should say:
__nullptr is always the Native Nullptr, regardless of whether the compilation is native, /clr, or /clr:pure.
std::nullptr_t is always the Native Nullptr's Type, regardless of whether the compilation is native, /clr, or /clr:pure. It is defined by <cstddef>. For good measure (as permitted but not required by C++0x), both <cstddef> and <stddef.h> define both std::nullptr_t and ::nullptr_t. JonCaves even made the compiler join the fun - it defines std::nullptr_t (but not ::nullptr_t) even in the absence of any headers whatsoever.
nullptr is the Native Nullptr when compiled as native (as required by C++0x).
nullptr is the Managed Nullptr Of Doom when compiled as /clr or /clr:pure (as required by the Necronomicon,
which is to say C++/CLI).
That said, I don't know how the Managed Nullptr works, except that I've heard that "it doesn't have a type",
a concept from which my mind recoils in fear. There's been a little talk of unifying these things,
but I don't think anybody has figured out what that would involve yet. When this came up I said:
> Regarding A Tale Of Two Nullptrs, I've heard several times that the problem is that the managed nullptr
"doesn't have a type", whatever that means, while the native nullptr has a type.
I don't know what it'd take to unify them (I assume that the native nullptr would have to "win",
since it cannot be infected with managedness, and conversions to managed stuff would have to be added to make
up the difference - and too bad for anyone who inspects nullptr closely and is surprised to discover that it
now has a type - I think all of those words in that order make sense), other than the fact that it
wasn't obviously simple enough for JonCaves to do in the first place.
> The Iron Nullptr Curtain causes more user suffering than STL suffering, though -
our only real interaction is in the definition of std::nullptr_t where we simply say decltype(__nullptr).
In the rest of our implementation, we just use NULL/0 (Dinkumware overwhelmingly prefers 0, as I recall).
In this case, perhaps the ambiguity in the docs is a reflection of the thing itself. If nullptr is always
the MNOD when compiled with /clr, then why does the compiler allow int* j to be initialized with it,
as shown in the code example referred to below? I don’t know why the docs say “the compiler cannot determine…”
because at least in these simple initializations it seems to know what to do (converting nullptr to nullptr_t
when the lhs is a native type, regardless of /clr?). But I’m curious what pain this causes or might cause for users.
Is there ever a case where the non-identity of these two things leaks out? If so, then we should really mention that
in the docs.
// mcpp_nullptr_6.cpp
// compile with: /clr
int main() {
int * i = 0;
int * j = nullptr; //what IS j now?
int^ k = nullptr; //the same or different?
}
C:\Temp>type doooooooom.cpp
#include <iostream>
#include <memory>
#include <ostream>
using namespace std;
int main() {
auto sp = make_shared<int *>(nullptr);
cout << sp.get() << endl;
}
C:\Temp>cl /EHsc /nologo /W4 doooooooom.cpp
doooooooom.cpp
C:\Temp>doooooooom
00227B2C
C:\Temp>cl /clr /nologo /W4 doooooooom.cpp
doooooooom.cpp
doooooooom.cpp(7) : error C2664: 'std::tr1::shared_ptr<_Ty> std::tr1::make_shared<int*,nullptr>(_Arg0 &&)' : cannot conv
ert parameter 1 from 'nullptr' to 'nullptr &&'
with
[
_Ty=int *,
_Arg0=nullptr
]
nullptr can only be converted to pointer or handle types
doooooooom.cpp(7) : fatal error C1903: unable to recover from previous error(s); stopping compilation