Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • doc/generic_types/generic_types.tex

    re2ef6bf r2b8a897  
    278278
    279279Finally, \CFA allows variable overloading:
    280 %\lstDeleteShortInline@%
    281 %\par\smallskip
    282 %\begin{tabular}{@{}l@{\hspace{1.5\parindent}}||@{\hspace{1.5\parindent}}l@{}}
     280\lstDeleteShortInline@%
     281\par\smallskip
     282\begin{tabular}{@{}l@{\hspace{1.5\parindent}}||@{\hspace{1.5\parindent}}l@{}}
    283283\begin{lstlisting}
    284284short int MAX = ...;
    285285int MAX = ...;
    286286double MAX = ...;
    287 short int s = MAX;  $\C{// select correct MAX}$
     287\end{lstlisting}
     288&
     289\begin{lstlisting}
     290short int s = MAX;  // select correct MAX
    288291int i = MAX;
    289292double d = MAX;
    290293\end{lstlisting}
    291 %\end{lstlisting}
    292 %&
    293 %\begin{lstlisting}
    294 %\end{tabular}
    295 %\smallskip\par\noindent
    296 %\lstMakeShortInline@%
     294\end{tabular}
     295\smallskip\par\noindent
     296\lstMakeShortInline@%
    297297Here, the single name @MAX@ replaces all the C type-specific names: @SHRT_MAX@, @INT_MAX@, @DBL_MAX@.
    298298As well, restricted constant overloading is allowed for the values @0@ and @1@, which have special status in C, \eg the value @0@ is both an integer and a pointer literal, so its meaning depends on context.
     
    585585Tuple flattening recursively expands a tuple into the list of its basic components.
    586586Tuple structuring packages a list of expressions into a value of tuple type, \eg:
    587 %\lstDeleteShortInline@%
    588 %\par\smallskip
    589 %\begin{tabular}{@{}l@{\hspace{1.5\parindent}}||@{\hspace{1.5\parindent}}l@{}}
     587\lstDeleteShortInline@%
     588\par\smallskip
     589\begin{tabular}{@{}l@{\hspace{1.5\parindent}}||@{\hspace{1.5\parindent}}l@{}}
    590590\begin{lstlisting}
    591591int f( int, int );
     
    593593int h( int, [int, int] );
    594594[int, int] x;
     595\end{lstlisting}
     596&
     597\begin{lstlisting}
    595598int y;
    596 f( x );                 $\C{// flatten}$
     599f( x );                 $\C[1in]{// flatten}$
    597600g( y, 10 );             $\C{// structure}$
    598 h( x, y );              $\C{// flatten and structure}$
    599 \end{lstlisting}
    600 %\end{lstlisting}
    601 %&
    602 %\begin{lstlisting}
    603 %\end{tabular}
    604 %\smallskip\par\noindent
    605 %\lstMakeShortInline@%
     601h( x, y );              $\C{// flatten and structure}\CRT{}$
     602\end{lstlisting}
     603\end{tabular}
     604\smallskip\par\noindent
     605\lstMakeShortInline@%
    606606In the call to @f@, @x@ is implicitly flattened so the components of @x@ are passed as the two arguments.
    607607In the call to @g@, the values @y@ and @10@ are structured into a single argument of type @[int, int]@ to match the parameter type of @g@.
     
    614614An assignment where the left side is a tuple type is called \emph{tuple assignment}.
    615615There are two kinds of tuple assignment depending on whether the right side of the assignment operator has a tuple type or a non-tuple type, called \emph{multiple} and \emph{mass assignment}, respectively.
    616 %\lstDeleteShortInline@%
    617 %\par\smallskip
    618 %\begin{tabular}{@{}l@{\hspace{1.5\parindent}}||@{\hspace{1.5\parindent}}l@{}}
     616\lstDeleteShortInline@%
     617\par\smallskip
     618\begin{tabular}{@{}l@{\hspace{1.5\parindent}}||@{\hspace{1.5\parindent}}l@{}}
    619619\begin{lstlisting}
    620620int x = 10;
    621621double y = 3.5;
    622622[int, double] z;
    623 z = [x, y];             $\C{// multiple assignment}$
     623
     624\end{lstlisting}
     625&
     626\begin{lstlisting}
     627z = [x, y];             $\C[1in]{// multiple assignment}$
    624628[x, y] = z;             $\C{// multiple assignment}$
    625629z = 10;                 $\C{// mass assignment}$
    626 [y, x] = 3.14;  $\C{// mass assignment}$
    627 \end{lstlisting}
    628 %\end{lstlisting}
    629 %&
    630 %\begin{lstlisting}
    631 %\end{tabular}
    632 %\smallskip\par\noindent
    633 %\lstMakeShortInline@%
     630[y, x] = 3.14;  $\C{// mass assignment}\CRT{}$
     631\end{lstlisting}
     632\end{tabular}
     633\smallskip\par\noindent
     634\lstMakeShortInline@%
    634635Both kinds of tuple assignment have parallel semantics, so that each value on the left and right side is evaluated before any assignments occur.
    635636As a result, it is possible to swap the values in two variables without explicitly creating any temporary variables or calling a function, \eg, @[x, y] = [y, x]@.
     
    655656Here, the mass assignment sets all members of @s@ to zero.
    656657Since tuple-index expressions are a form of member-access expression, it is possible to use tuple-index expressions in conjunction with member tuple expressions to manually restructure a tuple (\eg rearrange, drop, and duplicate components).
    657 %\lstDeleteShortInline@%
    658 %\par\smallskip
    659 %\begin{tabular}{@{}l@{\hspace{1.5\parindent}}||@{\hspace{1.5\parindent}}l@{}}
     658\lstDeleteShortInline@%
     659\par\smallskip
     660\begin{tabular}{@{}l@{\hspace{1.5\parindent}}||@{\hspace{1.5\parindent}}l@{}}
    660661\begin{lstlisting}
    661662[int, int, long, double] x;
    662663void f( double, long );
    663 x.[0, 1] = x.[1, 0];    $\C{// rearrange: [x.0, x.1] = [x.1, x.0]}$
    664 f( x.[0, 3] );            $\C{// drop: f(x.0, x.3)}$
    665 [int, int, int] y = x.[2, 0, 2]; $\C{// duplicate: [y.0, y.1, y.2] = [x.2, x.0.x.2]}$
    666 \end{lstlisting}
    667 %\end{lstlisting}
    668 %&
    669 %\begin{lstlisting}
    670 %\end{tabular}
    671 %\smallskip\par\noindent
    672 %\lstMakeShortInline@%
     664
     665\end{lstlisting}
     666&
     667\begin{lstlisting}
     668x.[0, 1] = x.[1, 0];    $\C[1in]{// rearrange: [x.0, x.1] = [x.1, x.0]}$
     669f( x.[0, 3] );            $\C{// drop: f(x.0, x.3)}\CRT{}$
     670[int, int, int] y = x.[2, 0, 2]; // duplicate: [y.0, y.1, y.2] = [x.2, x.0.x.2]
     671\end{lstlisting}
     672\end{tabular}
     673\smallskip\par\noindent
     674\lstMakeShortInline@%
    673675It is also possible for a member access to contain other member accesses, \eg:
    674676\begin{lstlisting}
     
    859861is transformed into:
    860862\begin{lstlisting}
     863// generated before the first 2-tuple
    861864forall(dtype T0, dtype T1 | sized(T0) | sized(T1)) struct _tuple2 {
    862         T0 field_0;                     $\C{// generated before the first 2-tuple}$
     865        T0 field_0;
    863866        T1 field_1;
    864867};
    865868_tuple2(int, int) f() {
    866869        _tuple2(double, double) x;
     870        // generated before the first 3-tuple
    867871        forall(dtype T0, dtype T1, dtype T2 | sized(T0) | sized(T1) | sized(T2)) struct _tuple3 {
    868                 T0 field_0;             $\C{// generated before the first 3-tuple}$
     872                T0 field_0;
    869873                T1 field_1;
    870874                T2 field_2;
     
    873877}
    874878\end{lstlisting}
    875 Tuple expressions are then simply converted directly into compound literals, \eg @[5, 'x', 1.24]@ becomes @(_tuple3(int, char, double)){ 5, 'x', 1.24 }@.
     879Tuple expressions are then simply converted directly into compound literals:
     880\begin{lstlisting}
     881[5, 'x', 1.24];
     882\end{lstlisting}
     883becomes:
     884\begin{lstlisting}
     885(_tuple3(int, char, double)){ 5, 'x', 1.24 };
     886\end{lstlisting}
    876887
    877888\begin{comment}
     
    938949In fact, \CFA's features for generic programming can enable faster runtime execution than idiomatic @void *@-based C code.
    939950This claim is demonstrated through a set of generic-code-based micro-benchmarks in C, \CFA, and \CC (see stack implementations in Appendix~\ref{sec:BenchmarkStackImplementation}).
    940 Since all these languages share a subset comprising standard C, maximal-performance benchmarks would show little runtime variance, other than in length and clarity of source code.
    941 A more illustrative benchmark is the idiomatic costs of each language's features covering common usage.
     951Since all these languages share a subset essentially comprising standard C, maximal-performance benchmarks would show little runtime variance, other than in length and clarity of source code.
     952A more illustrative benchmark measures the costs of idiomatic usage of each language's features.
    942953Figure~\ref{fig:BenchmarkTest} shows the \CFA benchmark tests for a generic stack based on a singly linked-list, a generic pair-data-structure, and a variadic @print@ routine similar to that in Section~\ref{sec:variadic-tuples}.
    943954The benchmark test is similar for C and \CC.
    944 The experiment uses element types @int@ and @pair(_Bool, char)@, and pushes $N=40M$ elements on a generic stack, copies the stack, clears one of the stacks, finds the maximum value in the other stack, and prints $N/2$ (to reduce graph height) constants.
     955The experiment uses element types @int@ and @pair(_Bool, char)@, and pushes $N=40M$ elements on a generic stack, copies the stack, clears one of the stacks, finds the maximum value in the other stack, and prints $N$ values of each type (2 per @print@ call).
     956
     957The structure of each benchmark implemented is: C with @void *@-based polymorphism, \CFA with the presented features, \CC with templates, and \CC using only class inheritance for polymorphism, called \CCV.
     958The \CCV variant illustrates an alternative object-oriented idiom where all objects inherit from a base @object@ class, mimicking a Java-like interface;
     959hence runtime checks are necessary to safely down-cast objects.
     960The most notable difference among the implementations is in memory layout of generic types: \CFA and \CC inline the stack and pair elements into corresponding list and pair nodes, while C and \CCV lack such a capability and instead must store generic objects via pointers to separately-allocated objects.
     961For the print benchmark, idiomatic printing is used: the C and \CFA variants used @stdio.h@, while the \CC and \CCV variants used @iostream@; preliminary tests show this distinction has negligible runtime impact.
     962Note, the C benchmark uses unchecked casts as there is no runtime mechanism to perform such checks, while \CFA and \CC provide type-safety statically.
    945963
    946964\begin{figure}
     
    974992\end{figure}
    975993
    976 The structure of each benchmark implemented is: C with @void *@-based polymorphism, \CFA with the presented features, \CC with templates, and \CC using only class inheritance for polymorphism, called \CCV.
    977 The \CCV variant illustrates an alternative object-oriented idiom where all objects inherit from a base @object@ class, mimicking a Java-like interface;
    978 hence runtime checks are necessary to safely down-cast objects.
    979 The most notable difference among the implementations is in memory layout of generic types: \CFA and \CC inline the stack and pair elements into corresponding list and pair nodes, while C and \CCV lack such a capability and instead must store generic objects via pointers to separately-allocated objects.
    980 For the print benchmark, idiomatic printing is used: the C and \CFA variants used @stdio.h@, while the \CC and \CCV variants used @iostream@; preliminary tests show this distinction has little runtime impact.
    981 Note, the C benchmark uses unchecked casts as there is no runtime mechanism to perform such checks, while \CFA and \CC provide type-safety statically.
    982 
    983994Figure~\ref{fig:eval} and Table~\ref{tab:eval} show the results of running the benchmark in Figure~\ref{fig:BenchmarkTest} and its C, \CC, and \CCV equivalents.
    984995The graph plots the median of 5 consecutive runs of each program, with an initial warm-up run omitted.
     
    10001011                                                                        & \CT{C}        & \CT{\CFA}     & \CT{\CC}      & \CT{\CCV}             \\ \hline
    10011012maximum memory usage (MB)                       & 10001         & 2502          & 2503          & 11253                 \\
    1002 source code size (lines)                        & 247           & 223           & 165           & 339                   \\
     1013source code size (lines)                        & 247           & 222           & 165           & 339                   \\
    10031014redundant type annotations (lines)      & 39            & 2                     & 2                     & 15                    \\
    10041015binary size (KB)                                        & 14            & 229           & 18            & 38                    \\
     
    10081019The C and \CCV variants are generally the slowest with the largest memory footprint, because of their less-efficient memory layout and the pointer-indirection necessary to implement generic types;
    10091020this inefficiency is exacerbated by the second level of generic types in the pair-based benchmarks.
    1010 By contrast, the \CFA and \CC variants run in roughly equivalent time for both the integer and pair of @_Bool@ and @char@ because the storage layout is equivalent.
     1021By contrast, the \CFA and \CC variants run in roughly equivalent time for both the integer and pair of @_Bool@ and @char@ because the storage layout is equivalent, with the inlined libraries (\ie no separate compilation) and greater maturity of the \CC compiler contributing to its lead.
    10111022\CCV is slower than C largely due to the cost of runtime type-checking of down-casts (implemented with @dynamic_cast@);
    10121023There are two outliers in the graph for \CFA: all prints and pop of @pair@.
    10131024Both of these cases result from the complexity of the C-generated polymorphic code, so that the GCC compiler is unable to optimize some dead code and condense nested calls.
    1014 A compiler for \CFA could easily perform these optimizations.
     1025A compiler designed for \CFA could easily perform these optimizations.
    10151026Finally, the binary size for \CFA is larger because of static linking with the \CFA libraries.
    10161027
    1017 \CC performs best because it uses header-only inlined libraries (\ie no separate compilation).
    1018 \CFA and \CC have the advantage of a pre-written generic @pair@ and @stack@ type to reduce line count, while C and \CCV require it to written by the programmer, as C does not have a generic collections-library and \CCV does not use the \CC standard template library by construction.
    1019 For \CCV, the definition of @object@ and wrapper classes for @bool@, @char@, @int@, and @const char *@ are included in the line count, which inflates its line count, as an actual object-oriented language would include these in the standard library;
     1028\CFA is also competitive in terms of source code size, measured as a proxy for programmer effort. The line counts in Table~\ref{tab:eval} include implementations of @pair@ and @stack@ types for all four languages for purposes of direct comparison, though it should be noted that \CFA and \CC have pre-written data structures in their standard libraries that programmers would generally use instead. Use of these standard library types has minimal impact on the performance benchmarks, but shrinks the \CFA and \CC benchmarks to 73 and 54 lines, respectively.
     1029On the other hand, C does not have a generic collections-library in its standard distribution, resulting in frequent reimplementation of such collection types by C programmers.
     1030\CCV does not use the \CC standard template library by construction, and in fact includes the definition of @object@ and wrapper classes for @bool@, @char@, @int@, and @const char *@ in its line count, which inflates this count somewhat, as an actual object-oriented language would include these in the standard library;
    10201031with their omission the \CCV line count is similar to C.
    10211032We justify the given line count by noting that many object-oriented languages do not allow implementing new interfaces on library types without subclassing or wrapper types, which may be similarly verbose.
     
    11651176std::forward_list wrapped in std::stack interface
    11661177
    1167 template<typename T> void print(ostream & out, const T & x) { out << x; }
    1168 template<> void print<bool>(ostream & out, const bool & x) { out << (x ? "true" : "false"); }
    1169 template<> void print<char>(ostream & out, const char & x ) { out << "'" << x << "'"; }
    1170 template<typename R, typename S> ostream & operator<< (ostream & out, const pair<R, S>& x) {
     1178template<typename T> void print(ostream& out, const T& x) { out << x; }
     1179template<> void print<bool>(ostream& out, const bool& x) { out << (x ? "true" : "false"); }
     1180template<> void print<char>(ostream& out, const char& x ) { out << "'" << x << "'"; }
     1181template<typename R, typename S> ostream& operator<< (ostream& out, const pair<R, S>& x) {
    11711182        out << "["; print(out, x.first); out << ", "; print(out, x.second); return out << "]"; }
    1172 template<typename T, typename... Args> void print(ostream & out, const T & arg, const Args &... rest) {
     1183template<typename T, typename... Args> void print(ostream& out, const T& arg, const Args&... rest) {
    11731184        out << arg;     print(out, rest...); }
    11741185\end{lstlisting}
     
    12091220forall(otype T) struct stack_node {
    12101221        T value;
    1211         stack_node(T) * next;
     1222        stack_node(T)* next;
    12121223};
    1213 forall(otype T) void ?{}(stack(T) * s) { (&s->head){ 0 }; }
    1214 forall(otype T) void ?{}(stack(T) * s, stack(T) t) {
    1215         stack_node(T) ** crnt = &s->head;
    1216         for ( stack_node(T) * next = t.head; next; next = next->next ) {
    1217                 *crnt = ((stack_node(T) *)malloc()){ next->value }; /***/
    1218                 stack_node(T) * acrnt = *crnt;
     1224forall(otype T) void ?{}(stack(T)* s) { (&s->head){ 0 }; }
     1225forall(otype T) void ?{}(stack(T)* s, stack(T) t) {
     1226        stack_node(T)** crnt = &s->head;
     1227        for ( stack_node(T)* next = t.head; next; next = next->next ) {
     1228                *crnt = ((stack_node(T)*)malloc()){ next->value }; /***/
     1229                stack_node(T)* acrnt = *crnt;
    12191230                crnt = &acrnt->next;
    12201231        }
    12211232        *crnt = 0;
    12221233}
    1223 forall(otype T) stack(T) ?=?(stack(T) * s, stack(T) t) {
     1234forall(otype T) stack(T) ?=?(stack(T)* s, stack(T) t) {
    12241235        if ( s->head == t.head ) return *s;
    12251236        clear(s);
     
    12271238        return *s;
    12281239}
    1229 forall(otype T) void ^?{}(stack(T) * s) { clear(s); }
    1230 forall(otype T) _Bool empty(const stack(T) * s) { return s->head == 0; }
    1231 forall(otype T) void push(stack(T) * s, T value) {
    1232         s->head = ((stack_node(T) *)malloc()){ value, s->head }; /***/
    1233 }
    1234 forall(otype T) T pop(stack(T) * s) {
    1235         stack_node(T) * n = s->head;
     1240forall(otype T) void ^?{}(stack(T)* s) { clear(s); }
     1241forall(otype T) _Bool empty(const stack(T)* s) { return s->head == 0; }
     1242forall(otype T) void push(stack(T)* s, T value) {
     1243        s->head = ((stack_node(T)*)malloc()){ value, s->head }; /***/
     1244}
     1245forall(otype T) T pop(stack(T)* s) {
     1246        stack_node(T)* n = s->head;
    12361247        s->head = n->next;
    12371248        T x = n->value;
     
    12401251        return x;
    12411252}
    1242 forall(otype T) void clear(stack(T) * s) {
    1243         for ( stack_node(T) * next = s->head; next; ) {
    1244                 stack_node(T) * crnt = next;
     1253forall(otype T) void clear(stack(T)* s) {
     1254        for ( stack_node(T)* next = s->head; next; ) {
     1255                stack_node(T)* crnt = next;
    12451256                next = crnt->next;
    12461257                delete(crnt);
     
    12561267        struct node {
    12571268                T value;
    1258                 node * next;
    1259                 node( const T & v, node * n = nullptr ) : value(v), next(n) {}
     1269                node* next;
     1270                node( const T& v, node* n = nullptr ) : value(v), next(n) {}
    12601271        };
    1261         node * head;
     1272        node* head;
    12621273        void copy(const stack<T>& o) {
    1263                 node ** crnt = &head;
    1264                 for ( node * next = o.head;; next; next = next->next ) {
     1274                node** crnt = &head;
     1275                for ( node* next = o.head;; next; next = next->next ) {
    12651276                        *crnt = new node{ next->value }; /***/
    12661277                        crnt = &(*crnt)->next;
     
    12711282        stack() : head(nullptr) {}
    12721283        stack(const stack<T>& o) { copy(o); }
    1273         stack(stack<T> && o) : head(o.head) { o.head = nullptr; }
     1284        stack(stack<T>&& o) : head(o.head) { o.head = nullptr; }
    12741285        ~stack() { clear(); }
    1275         stack & operator= (const stack<T>& o) {
     1286        stack& operator= (const stack<T>& o) {
    12761287                if ( this == &o ) return *this;
    12771288                clear();
     
    12791290                return *this;
    12801291        }
    1281         stack & operator= (stack<T> && o) {
     1292        stack& operator= (stack<T>&& o) {
    12821293                if ( this == &o ) return *this;
    12831294                head = o.head;
     
    12861297        }
    12871298        bool empty() const { return head == nullptr; }
    1288         void push(const T & value) { head = new node{ value, head };  /***/ }
     1299        void push(const T& value) { head = new node{ value, head };  /***/ }
    12891300        T pop() {
    1290                 node * n = head;
     1301                node* n = head;
    12911302                head = n->next;
    12921303                T x = std::move(n->value);
     
    12951306        }
    12961307        void clear() {
    1297                 for ( node * next = head; next; ) {
    1298                         node * crnt = next;
     1308                for ( node* next = head; next; ) {
     1309                        node* crnt = next;
    12991310                        next = crnt->next;
    13001311                        delete crnt;
     
    13091320\begin{lstlisting}[xleftmargin=2\parindentlnth,aboveskip=0pt,belowskip=0pt]
    13101321struct stack_node {
    1311         void * value;
    1312         struct stack_node * next;
     1322        void* value;
     1323        struct stack_node* next;
    13131324};
    13141325struct stack new_stack() { return (struct stack){ NULL }; /***/ }
    1315 void copy_stack(struct stack * s, const struct stack * t, void * (*copy)(const void *)) {
    1316         struct stack_node ** crnt = &s->head;
    1317         for ( struct stack_node * next = t->head; next; next = next->next ) {
     1326void copy_stack(struct stack* s, const struct stack* t, void* (*copy)(const void*)) {
     1327        struct stack_node** crnt = &s->head;
     1328        for ( struct stack_node* next = t->head; next; next = next->next ) {
    13181329                *crnt = malloc(sizeof(struct stack_node)); /***/
    13191330                **crnt = (struct stack_node){ copy(next->value) }; /***/
     
    13221333        *crnt = 0;
    13231334}
    1324 _Bool stack_empty(const struct stack * s) { return s->head == NULL; }
    1325 void push_stack(struct stack * s, void * value) {
    1326         struct stack_node * n = malloc(sizeof(struct stack_node)); /***/
     1335_Bool stack_empty(const struct stack* s) { return s->head == NULL; }
     1336void push_stack(struct stack* s, void* value) {
     1337        struct stack_node* n = malloc(sizeof(struct stack_node)); /***/
    13271338        *n = (struct stack_node){ value, s->head }; /***/
    13281339        s->head = n;
    13291340}
    1330 void * pop_stack(struct stack * s) {
    1331         struct stack_node * n = s->head;
     1341void* pop_stack(struct stack* s) {
     1342        struct stack_node* n = s->head;
    13321343        s->head = n->next;
    1333         void * x = n->value;
     1344        void* x = n->value;
    13341345        free(n);
    13351346        return x;
    13361347}
    1337 void clear_stack(struct stack * s, void (*free_el)(void *)) {
    1338         for ( struct stack_node * next = s->head; next; ) {
    1339                 struct stack_node * crnt = next;
     1348void clear_stack(struct stack* s, void (*free_el)(void*)) {
     1349        for ( struct stack_node* next = s->head; next; ) {
     1350                struct stack_node* crnt = next;
    13401351                next = crnt->next;
    13411352                free_el(crnt->value);
     
    13491360\CCV
    13501361\begin{lstlisting}[xleftmargin=2\parindentlnth,aboveskip=0pt,belowskip=0pt]
    1351 stack::node::node( const object & v, node * n ) : value( v.new_copy() ), next( n ) {}
    1352 void stack::copy(const stack & o) {
    1353         node ** crnt = &head;
    1354         for ( node * next = o.head; next; next = next->next ) {
     1362stack::node::node( const object& v, node* n ) : value( v.new_copy() ), next( n ) {}
     1363void stack::copy(const stack& o) {
     1364        node** crnt = &head;
     1365        for ( node* next = o.head; next; next = next->next ) {
    13551366                *crnt = new node{ *next->value };
    13561367                crnt = &(*crnt)->next;
     
    13591370}
    13601371stack::stack() : head(nullptr) {}
    1361 stack::stack(const stack & o) { copy(o); }
    1362 stack::stack(stack && o) : head(o.head) { o.head = nullptr; }
     1372stack::stack(const stack& o) { copy(o); }
     1373stack::stack(stack&& o) : head(o.head) { o.head = nullptr; }
    13631374stack::~stack() { clear(); }
    1364 stack & stack::operator= (const stack & o) {
     1375stack& stack::operator= (const stack& o) {
    13651376        if ( this == &o ) return *this;
    13661377        clear();
     
    13681379        return *this;
    13691380}
    1370 stack & stack::operator= (stack && o) {
     1381stack& stack::operator= (stack&& o) {
    13711382        if ( this == &o ) return *this;
    13721383        head = o.head;
     
    13751386}
    13761387bool stack::empty() const { return head == nullptr; }
    1377 void stack::push(const object & value) { head = new node{ value, head }; /***/ }
     1388void stack::push(const object& value) { head = new node{ value, head }; /***/ }
    13781389ptr<object> stack::pop() {
    1379         node * n = head;
     1390        node* n = head;
    13801391        head = n->next;
    13811392        ptr<object> x = std::move(n->value);
     
    13841395}
    13851396void stack::clear() {
    1386         for ( node * next = head; next; ) {
    1387                 node * crnt = next;
     1397        while ( node* next = head; next; ) {
     1398                node* crnt = next;
    13881399                next = crnt->next;
    13891400                delete crnt;
Note: See TracChangeset for help on using the changeset viewer.