1 | // Copyright David Abrahams 2009. Distributed under the Boost |
2 | // Software License, Version 1.0. (See accompanying |
3 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
4 | |
5 | #include <iostream> |
6 | #include <boost/core/lightweight_test.hpp> |
7 | |
8 | #ifdef NO_MOVE |
9 | # undef BOOST_COPY_ASSIGN_REF |
10 | # define BOOST_COPY_ASSIGN_REF(X) X const& |
11 | # undef BOOST_COPYABLE_AND_MOVABLE |
12 | # define BOOST_COPYABLE_AND_MOVABLE(X) |
13 | # define MOVE(x) (x) |
14 | #else |
15 | #include <boost/move/utility_core.hpp> |
16 | # define MOVE(x) boost::move(x) |
17 | #endif |
18 | |
19 | struct X |
20 | { |
21 | X() : id(instances++) |
22 | { |
23 | std::cout << "X" << id << ": construct\n" ; |
24 | } |
25 | |
26 | X(X const& rhs) : id(instances++) |
27 | { |
28 | std::cout << "X" << id << ": <- " << "X" << rhs.id << ": **copy**\n" ; |
29 | ++copies; |
30 | } |
31 | |
32 | // This particular test doesn't exercise assignment, but for |
33 | // completeness: |
34 | X& operator=(BOOST_COPY_ASSIGN_REF(X) rhs) |
35 | { |
36 | std::cout << "X" << id << ": <- " << "X" << rhs.id << ": assign\n" ; |
37 | return *this; |
38 | } |
39 | |
40 | #ifndef NO_MOVE |
41 | X& operator=(BOOST_RV_REF(X) rhs) |
42 | { |
43 | std::cout << "X" << id << ": <- " << "X" << rhs.id << ": move assign\n" ; |
44 | return *this; |
45 | } |
46 | |
47 | X(BOOST_RV_REF(X) rhs) : id(instances++) |
48 | { |
49 | std::cout << "X" << id << ": <- " << "X" << rhs.id << ": ..move construct..\n" ; |
50 | ++copies; |
51 | } |
52 | #endif |
53 | |
54 | ~X() { std::cout << "X" << id << ": destroy\n" ; } |
55 | |
56 | unsigned id; |
57 | |
58 | static unsigned copies; |
59 | static unsigned instances; |
60 | |
61 | BOOST_COPYABLE_AND_MOVABLE(X) |
62 | }; |
63 | |
64 | unsigned X::copies = 0; |
65 | unsigned X::instances = 0; |
66 | |
67 | #define CHECK_COPIES( stmt, min, max, comment ) \ |
68 | { \ |
69 | unsigned const old_copies = X::copies; \ |
70 | \ |
71 | std::cout << "\n" comment "\n" #stmt "\n===========\n"; \ |
72 | { \ |
73 | stmt; \ |
74 | } \ |
75 | unsigned const n = X::copies - old_copies; \ |
76 | volatile unsigned const minv(min), maxv(max); \ |
77 | BOOST_TEST(n <= maxv); \ |
78 | if (n > maxv) \ |
79 | std::cout << "*** max is too low or compiler is buggy ***\n"; \ |
80 | BOOST_TEST(n >= minv); \ |
81 | if (n < minv) \ |
82 | std::cout << "*** min is too high or compiler is buggy ***\n"; \ |
83 | \ |
84 | std::cout << "-----------\n" \ |
85 | << n << "/" << max \ |
86 | << " possible copies/moves made\n" \ |
87 | << max - n << "/" << max - min \ |
88 | << " possible elisions performed\n\n"; \ |
89 | \ |
90 | if (n > minv) \ |
91 | std::cout << "*** " << n - min \ |
92 | << " possible elisions missed! ***\n"; \ |
93 | } |
94 | |
95 | struct trace |
96 | { |
97 | trace(char const* name) |
98 | : m_name(name) |
99 | { |
100 | std::cout << "->: " << m_name << "\n" ; |
101 | } |
102 | |
103 | ~trace() |
104 | { |
105 | std::cout << "<-: " << m_name << "\n" ; |
106 | } |
107 | |
108 | char const* m_name; |
109 | }; |
110 | |
111 | void sink(X) |
112 | { |
113 | trace t("sink" ); |
114 | } |
115 | |
116 | X nrvo_source() |
117 | { |
118 | trace t("nrvo_source" ); |
119 | X a; |
120 | return a; |
121 | } |
122 | |
123 | X urvo_source() |
124 | { |
125 | trace t("urvo_source" ); |
126 | return X(); |
127 | } |
128 | |
129 | X identity(X a) |
130 | { |
131 | trace t("identity" ); |
132 | return a; |
133 | } |
134 | |
135 | X lvalue_; |
136 | X& lvalue() |
137 | { |
138 | return lvalue_; |
139 | } |
140 | typedef X rvalue; |
141 | |
142 | X ternary( bool y ) |
143 | { |
144 | X a, b; |
145 | return MOVE(y?a:b); |
146 | } |
147 | |
148 | int main(int argc, char* argv[]) |
149 | { |
150 | ::boost::movelib::ignore(argv); |
151 | // Double parens prevent "most vexing parse" |
152 | CHECK_COPIES( X a(( lvalue() )), 1U, 1U, "Direct initialization from lvalue" ); |
153 | CHECK_COPIES( X a(( rvalue() )), 0U, 1U, "Direct initialization from rvalue" ); |
154 | |
155 | CHECK_COPIES( X a = lvalue(), 1U, 1U, "Copy initialization from lvalue" ); |
156 | CHECK_COPIES( X a = rvalue(), 0U, 1U, "Copy initialization from rvalue" ); |
157 | |
158 | CHECK_COPIES( sink( lvalue() ), 1U, 1U, "Pass lvalue by value" ); |
159 | CHECK_COPIES( sink( rvalue() ), 0U, 1U, "Pass rvalue by value" ); |
160 | |
161 | CHECK_COPIES( nrvo_source(), 0U, 1U, "Named return value optimization (NRVO)" ); |
162 | CHECK_COPIES( urvo_source(), 0U, 1U, "Unnamed return value optimization (URVO)" ); |
163 | |
164 | // Just to prove these things compose properly |
165 | CHECK_COPIES( X a(urvo_source()), 0U, 2U, "Return value used as ctor arg" ); |
166 | |
167 | // Expect to miss one possible elision here |
168 | CHECK_COPIES( identity( rvalue() ), 0U, 2U, "Return rvalue passed by value" ); |
169 | |
170 | // Expect to miss an elision in at least one of the following lines |
171 | CHECK_COPIES( X a = ternary( argc == 1000 ), 0U, 2U, "Return result of ternary operation" ); |
172 | CHECK_COPIES( X a = ternary( argc != 1000 ), 0U, 2U, "Return result of ternary operation again" ); |
173 | return boost::report_errors(); |
174 | } |
175 | |