mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-12-22 13:18:28 +00:00
cf9112a3a3
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@35862 a592a061-630c-0410-9148-cb99ea01b6c8
1810 lines
33 KiB
Plaintext
1810 lines
33 KiB
Plaintext
#LyX 2.0.0svn created this file. For more info see http://www.lyx.org/
|
|
\lyxformat 345
|
|
\begin_document
|
|
\begin_header
|
|
\textclass scrbook
|
|
\use_default_options true
|
|
\language english
|
|
\inputencoding auto
|
|
\font_roman default
|
|
\font_sans default
|
|
\font_typewriter default
|
|
\font_default_family default
|
|
\font_sc false
|
|
\font_osf false
|
|
\font_sf_scale 100
|
|
\font_tt_scale 100
|
|
\graphics default
|
|
\paperfontsize default
|
|
\spacing single
|
|
\use_hyperref false
|
|
\papersize default
|
|
\use_geometry false
|
|
\use_amsmath 1
|
|
\use_esint 1
|
|
\cite_engine basic
|
|
\use_bibtopic false
|
|
\paperorientation portrait
|
|
\secnumdepth 3
|
|
\tocdepth 3
|
|
\paragraph_separation indent
|
|
\quotes_language english
|
|
\papercolumns 1
|
|
\papersides 1
|
|
\paperpagestyle default
|
|
\listings_params "basicstyle={\footnotesize}"
|
|
\tracking_changes true
|
|
\output_changes false
|
|
\author "Kornel Benko" Kornel.Benko@berlin.de
|
|
\end_header
|
|
|
|
\begin_body
|
|
|
|
\begin_layout Title
|
|
LyX Development
|
|
\begin_inset Newline newline
|
|
\end_inset
|
|
|
|
Rules and Recommendations
|
|
\end_layout
|
|
|
|
\begin_layout Chapter
|
|
Rules for the code in LyX
|
|
\begin_inset Foot
|
|
status collapsed
|
|
|
|
\begin_layout Plain Layout
|
|
Updated from the C++STYLE distributed with the GNU C++ Standard.
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
The aim of this file is to serve as a guide for the developers, to aid us
|
|
to get clean and uniform code.
|
|
This document is incomplete.
|
|
We really like to have new developers joining the LyX Project.
|
|
However, we have had problems in the past with developers leaving the project
|
|
and their contributed code in a far from perfect state.
|
|
Most of this happened before we really became aware of these issues, but
|
|
still, we don't want it to happen again.
|
|
So we have put together some guidelines and rules for the developers.
|
|
\end_layout
|
|
|
|
\begin_layout Section
|
|
General
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
These guidelines should save us a lot of work while cleaning up the code
|
|
and help us to have quality code.
|
|
LyX has been haunted by problems coming from unfinished projects by people
|
|
who have left the team.
|
|
Those problems will hopefully disappear if the code is easy to hand over
|
|
to somebody else.
|
|
In general, if you want to contribute to the main source, we expect at
|
|
least that you:
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
the most important rule first: KISS (Keep It Simple Stupid), always use
|
|
a simple implementation in favor of a more complicated one.
|
|
This eases maintenance a lot.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
write good C++ code: Readable, well commented and taking advantage of the
|
|
OO model.
|
|
Follow the formatting guidelines.
|
|
See Formatting.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
adapt the code to the structures already existing in LyX, or in the case
|
|
that you have better ideas, discuss them on the developer's list before
|
|
writing the code.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
take advantage of the C++ standard library.
|
|
Especially don't use custom containers when a standard container is usable;
|
|
learn to use the algorithms and functors in the standard library.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
be aware of exceptions and write exception safe code.
|
|
See Exceptions.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
document all variables, methods, functions, classes etc.
|
|
We are using the source documentation program doxygen, a program that handles
|
|
javadoc syntax, to document sources.
|
|
You can download doxygen from: http://www.stack.nl/~dimitri/doxygen/
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
we have certain code constructs that we try to follow.
|
|
See Code Constructs.
|
|
\end_layout
|
|
|
|
\begin_layout Section
|
|
Submitting Code
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
It is implicitly understood that all patches contributed to The LyX Project
|
|
is under the Gnu General Public License, version 2 or later.
|
|
If you have a problem with that, don't contribute code.
|
|
Also please don't just pop up out of the blue with a huge patch (or small)
|
|
that changes something substantial in LyX.
|
|
Always discuss your ideas with the developers on the developer's mailing
|
|
list.
|
|
When you create the patch, please use "diff -up" since we find that a lot
|
|
easier to read than the other diff formats.
|
|
Also please do not send patches that implements or fixes several different
|
|
things; several patches is a much better option.
|
|
We also require you to provide a commit message entry with every patch,
|
|
this describes in detail what the patch is doing.
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Section
|
|
Code Constructs
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
We have several guidelines on code constructs, some of these exist to make
|
|
the code faster, others to make the code clearer.
|
|
Yet others exist to allow us to take advantage of the strong type checking
|
|
in C++.
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Declaration of variables should wait as long as possible.
|
|
The rule is: "Don't declare it until you need it." In C++ there are a lot
|
|
of user defined types, and these can very often be expensive to initialize.
|
|
This rule connects to the next rule too.
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Declare the variable as const if you don't need to change it.
|
|
This applies to POD types like int as well as classes.
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Make the scope of a variable as small as possible.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Make good use of namespaces.
|
|
Prefer anonymous namespaces to declaring "static" for file scope.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Prefer preincrement to postincrement whenever possible.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Preincrement has potential of being faster than postincrement.
|
|
Just think about the obvious implementations of pre/post-increment.
|
|
This rule applies to decrement too.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Use:
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
lstparams "basicstyle={\footnotesize},language={C++}"
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
++T;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
--U;
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
Do not use:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
T++; // not used in LyX
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
U--;// not used in LyX
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
Try to minimize evaluation of the same code over and over.
|
|
This is aimed especially at loops.
|
|
|
|
\begin_inset Newline newline
|
|
\end_inset
|
|
|
|
Use:
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
Container::iterator end = large.end();
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
for (Container::iterator it = large.begin(); it != end; ++it) {
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
...;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
}
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
Do not use:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
for (Container::iterator it = large.begin(); it != large.end(); ++it) {
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
...;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
}
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
For functions and methods that return a non-POD type
|
|
\begin_inset Foot
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
Plain Ol' Data type
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
T, return T const instead.
|
|
This gives better type checking, and will give a compiler warning when
|
|
temporaries are used wrongly.
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Standard
|
|
Use:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
T const add(..);
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
Do not use:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
T add(..);
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
Avoid using the default cases in switch statements unless you have too.
|
|
Use the correct type for the switch expression and let the compiler ensure
|
|
that all cases are exhausted.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
enum Foo {
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
FOO_BAR1,
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
FOO_BAR2
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
};
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
Foo f = ...;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
switch (f) {
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
case FOO_BAR1: ...;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
break;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
case FOO_BAR2: ...;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
break;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
default: ...;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
// not needed and would shadow a wrong use of Foo
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
break;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
}
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Section
|
|
Exceptions
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
Be aware of the presence of exceptions.
|
|
One important thing to realize is that you often do not have to use throw,
|
|
try or catch to be exception safe.
|
|
Let's look at the different types of exceptions safety: (These are taken
|
|
from Herb Sutter's book[ExC++]
|
|
\end_layout
|
|
|
|
\begin_layout Enumerate
|
|
Basic guarantee: Even in the presence of exceptions thrown by T or other
|
|
exceptions, Stack objects don't leak resources.
|
|
Note that this also implies that the container will be destructible and
|
|
usable even if an exception is thrown while performing some container operation.
|
|
However, if an exception is thrown, the container will be in a consistent,
|
|
but not necessarily predictable, state.
|
|
Containers that support the basic guarantee can work safely in some settings.
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Enumerate
|
|
Strong guarantee: If an operation terminates because of an exception, program
|
|
state will remain unchanged.
|
|
This always implies commit-or-rollback semantics, including that no references
|
|
or iterators into the container be invalidated if an operation fails.
|
|
For example, if a Stack client calls Top and then attempts a Push that
|
|
fails because of an exception, then the state of the Stack object must
|
|
be unchanged and the reference returned from the prior call to Top must
|
|
still be valid.
|
|
For more information on these guarantees, see Dave Abrahams's documentation
|
|
of the SGI exception-safe standard library adaption at: http://www.stlport.org/do
|
|
c/exception_safety.html Probably the most interesting point here is that
|
|
when you implement the basic guarantee, the strong guarantee often comes
|
|
for free.
|
|
For example, in our Stack implementation, almost everything we did was
|
|
needed to satisfy just the basic guarantee -- and what's presented above
|
|
very nearly satisfies the strong guarantee, with little o
|
|
\change_inserted 0 1288157484
|
|
r
|
|
\change_deleted 0 1288157483
|
|
f
|
|
\change_unchanged
|
|
no extra work.
|
|
Not half bad, considering all the trouble we went to.
|
|
In addition to these two guarantees, there is one more guarantee that certain
|
|
functions must provide in order to make overall exception safety possible:
|
|
\end_layout
|
|
|
|
\begin_layout Enumerate
|
|
No throw guarantee: The function will not emit an exception under any circumstan
|
|
ces.
|
|
Overall exception safety isn't possible unless certain functions are guaranteed
|
|
not to throw.
|
|
In particular, we've seen that this is true for destructors; later in this
|
|
miniseries, we'll see that it's also needed in certain helper functions,
|
|
such as Swap().
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
For all cases where we might be able to write exception safe functions without
|
|
using try, throw or catch we should do so.
|
|
In particular we should look over all destructors to ensure that they are
|
|
as exception safe as possible.
|
|
\end_layout
|
|
|
|
\begin_layout Section
|
|
Formatting
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Only one declaration on each line.
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Standard
|
|
Use:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
int a;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
int b;
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
Do not use:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
int a,b; // not used in LyX
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
This is especially important when initialization is done at the same time:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
Use:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
string a = "Lars";
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
string b = "Gullik";
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
Do not use:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
string a = "Lars", b = "Gullik"; // not used in LyX
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
[Note that 'string a = "Lars"' is formally calling a copy constructor on
|
|
a temporary constructed from a string literal and therefore has the potential
|
|
of being more expensive then direct construction by 'string a("Lars")'.
|
|
However the compiler is allowed to elide the copy (even if it had side
|
|
effects), and modern compilers typically do so.
|
|
Given these equal costs, LyX code favours the '=' idiom as it is in line
|
|
with the traditional C-style initialization, _and_ cannot be mistaken as
|
|
function declaration, _and_ reduces the level of nested parantheses in
|
|
more initializations.]
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
Pointers and references:
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Standard
|
|
Use:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
char * p = "flop";
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
char & c = *p;
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
Do not use:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
char *p = "flop"; // not used in LyX
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
char &c = *p; // not used in LyX
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
Some time ago we had a huge discussion on this subject and after convincing
|
|
argumentation from Asger this is what we decided.
|
|
Also note that we will have:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
char const * p;
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
and not
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
const char * p; // not used in LyX
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
Operator names and parentheses
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
operator==(type)
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
and not
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
operator == (type) // not used in LyX
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
The == is part of the function name, separating it makes the declaration
|
|
look like an expression.
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
Function names and parentheses
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
void mangle()
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
and not
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
void mangle () // not used in LyX
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
Enumerators
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
enum Foo {
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
FOO_ONE = 1,
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
FOO_TWO = 2,
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
FOO_THREE = 3
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
};
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
and not
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
enum { one = 1, two = 2, three 3 }; // not used in LyX
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
and not
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
enum {
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
One = 1,
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
Two = 2,
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
Three = 3
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
};
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
Null pointers
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Standard
|
|
Using a plain 0 is always correct and least effort to type.
|
|
So:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
void * p = 0;
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
and not
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
void * p = NULL; // not used in LyX
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
and not
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
void * p = '
|
|
\backslash
|
|
0'; // not used in LyX
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
and not
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
void * p = 42 - 7 * 6; // not used in LyX
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
Note: As an exception, imported third party code as well as code interfacing
|
|
the "native" APIs (src/support/os_*) can use NULL.
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
Naming rules for classes
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Itemize
|
|
Use descriptive but simple and short names.
|
|
Do not abbreviate.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Class names are usually capitalized, and function names lowercased.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Enums are named like Classes, values are usually in lower-case.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Public API is camel-case ('void setAFlagToAValue(bool)')
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Members variables are underscored ('enable_this_feature_flag_') with a final
|
|
'_'
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Private/protected functions are also camel-case
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
New types are capitalized, so this goes for typedefs, classes, structs and
|
|
enums.
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
Formatting
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Itemize
|
|
Adapt the formatting of your code to the one used in the other parts of
|
|
LyX.
|
|
In case there is different formatting for the same construct, use the one
|
|
used more often.
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
Use existing structures
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Itemize
|
|
|
|
\change_inserted 0 1288159254
|
|
\begin_inset CommandInset label
|
|
LatexCommand label
|
|
name "Use-string-wherever"
|
|
|
|
\end_inset
|
|
|
|
|
|
\change_unchanged
|
|
Use string wherever possible.
|
|
LyX will someday move to Unicode, and that will be easy if everybody uses
|
|
string now.
|
|
Unicode strings should prefer using docstring instead of UTF-8 encoded
|
|
std::string.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Check out the filename and path tools in filetools.h
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Check out the string tools in lstring.h.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Use the LyXErr class to report errors and messages using the lyxerr instantiatio
|
|
n.
|
|
[add description of other existing structures]
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
Declarations
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Itemize
|
|
Use this order for the access sections of your class: public, protected,
|
|
private.
|
|
The public section is interesting for every user of the class.
|
|
The private section is only of interest for the implementors of the class
|
|
(you).
|
|
[Obviously not true since this is for developers, and we do not want one
|
|
developer only to be able to read and understand the implementation of
|
|
class internals.
|
|
Lgb]
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Avoid declaring global objects in the declaration file of the class.
|
|
If the same variable is used for all objects, use a static member.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Avoid global or static variables.
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
File headers
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Standard
|
|
If you create a new file, the top of the file should look something like
|
|
this :
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
/**
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
*
|
|
\backslash
|
|
file NewFile.cpp
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
* This file is part of LyX, the document processor.
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
* Licence details can be found in the file COPYING.
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
*
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
*
|
|
\backslash
|
|
author Kaiser Sose
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
*
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
* Full author contact details are available in file CREDITS
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
*/
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
Documentation
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Itemize
|
|
The documentation is generated from the header files.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
You document for the other developers, not for yourself.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
You should document what the function does, not the implementation.
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Itemize
|
|
in the .cpp files you document the implementation.
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
Single line description (///), multiple lines description (/** ...
|
|
*/) see the doxygen webpage referenced above
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Section
|
|
Naming rules for Lyx User Functions (LFUNs)
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
Here is the set of rules to apply when a new command name is introduced:
|
|
\end_layout
|
|
|
|
\begin_layout Enumerate
|
|
Use the object.event order.
|
|
That is, use `word-forward' instead of`forward-word'.
|
|
\end_layout
|
|
|
|
\begin_layout Enumerate
|
|
Don't introduce an alias for an already named object.
|
|
Same for events.
|
|
\end_layout
|
|
|
|
\begin_layout Enumerate
|
|
Forward movement or focus is called `forward' (not `right').
|
|
\end_layout
|
|
|
|
\begin_layout Enumerate
|
|
Backward movement or focus is called `backward' (not `left').
|
|
\end_layout
|
|
|
|
\begin_layout Enumerate
|
|
Upward movement of focus is called `up'.
|
|
\end_layout
|
|
|
|
\begin_layout Enumerate
|
|
Downward movement is called `down'.
|
|
\end_layout
|
|
|
|
\begin_layout Enumerate
|
|
The begin of an object is called `begin' (not `start').
|
|
\end_layout
|
|
|
|
\begin_layout Enumerate
|
|
The end of an object is called `end'.
|
|
\end_layout
|
|
|
|
\begin_layout Section
|
|
How to create class interfaces
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
(a.k.a How Non-Member Functions Improve Encapsulation)
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
I recently read an article by Scott Meyers
|
|
\change_deleted 0 1288158556
|
|
in
|
|
\change_unchanged
|
|
, where he makes a strong case on how non-member functions makes classes
|
|
more encapsulated, not less.
|
|
Just skipping to the core of this provides us with the following algorithm
|
|
for deciding what kind of function to add to a class interface:
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
We need to add a function f to the class C's API.
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
if (f needs to be virtual)
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
make f a member function of C;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
else if (f is operator>> or operator<<) {
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
make f a non-member function;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
if (f needs access to non-public members of C)
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
make f a friend of C;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
} else if (f needs type conversions on its left-most argument) {
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
make f a non-member function;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
if (f needs access to non-public members of C)
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
make f a friend of C;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
} else if (f can be implemented via C's public interface)
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
make f a non-member function;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
else
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
make f a member function of C;
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Chapter
|
|
Recommendations
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
These are some rules for effective C++ programming.
|
|
These are taken from Scott Meyers
|
|
\begin_inset CommandInset citation
|
|
LatexCommand cite
|
|
key "key-4"
|
|
|
|
\end_inset
|
|
|
|
, and are presented in their short form.
|
|
These are not all the rules Meyers presents, only the most important of
|
|
them.
|
|
LyX does not yet follow these rules, but they should be the goal.
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
use const and inline instead of #define
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
use the same form in corresponding calls to new and delete, i.e.
|
|
write delete[] obj; if new obj[n]; was used to create the object and write
|
|
delete obj; if you wrote new obj; Notice strings should be std::string's
|
|
instead of char *'s.
|
|
|
|
\change_inserted 0 1288159280
|
|
(this contradicts to
|
|
\begin_inset CommandInset ref
|
|
LatexCommand ref
|
|
reference "Use-string-wherever"
|
|
|
|
\end_inset
|
|
|
|
)
|
|
\change_unchanged
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
define a default constructor, copy constructor and an assignment operator
|
|
for all classes with dynamically allocated memory that are not made noncopyable
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
do not define default constructor, copy constructor and an assignment operator
|
|
if the compiler generated one would do the same
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
make destructors virtual in base classes and only there
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
assign to all data members in operator=()
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
strive for class interfaces that are complete and minimal
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
differentiate among member functions, global functions and friend functions
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
avoid data members in the public interface
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
use const whenever possible
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
pass and return objects by reference instead of by value
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
choose carefully between function overloading and parameter defaulting
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
never return a reference to a local object or a dereferenced pointer initialized
|
|
by new within the function
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
use enums for integral constants
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
minimize compilation dependencies between files
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
pay attention to compiler warnings
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
differentiate between inheritance of interface and inheritance of implementation
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
differentiate between inheritance and templates
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
ensure that global objects are initialized before they are used
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
avoid conditions to 'if' and 'while' that span more than a line
|
|
\end_layout
|
|
|
|
\begin_layout Chapter
|
|
\start_of_appendix
|
|
Notes
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
And one of mine: (Lgb)
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Itemize
|
|
when swi
|
|
\change_inserted 0 1288159389
|
|
t
|
|
\change_unchanged
|
|
ching on enums, refrain from using "default:" if possible
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
And one of mine: (Andre')
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Itemize
|
|
try to implement your class in a way that the automatically generated copy
|
|
constructor and copy assignment work out-of-the box
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
I don't have problems with using boost in the implementation _if and only
|
|
if_ it provides actual benefits over less intrusive alternatives.
|
|
I do have a problem with needlessly sprinkling 'boost::' over interfaces,
|
|
especially if it does not add any value.
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Standard
|
|
Given that there seems to be an unconditional "typedef unsigned int quint32;"
|
|
in qglobal.h I don't think there's any platform supported by current LyX
|
|
that could not use 'unsigned int' (and an static assert in some implementation
|
|
file for the unlikely case some ILP64 zombie raises its ugly head again.
|
|
And if that happens, using <cstdint> would still be a better choice...)
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
The idea is to create something that's not compilable as soon as the condition
|
|
is violated.
|
|
There are lots of possibilities to achieve this, some examples follow:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
In C++0x there's a "built-in":
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
static_assert(sizeof(int) == 4, "Funny platform")
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
until then on namespace scope:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
#include <boost/static_assert.hpp> BOOST_STATIC_ASSERT(sizeof(int) == 4)
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
or without boost:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
template<bool Condition> struct static_assert_helper;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
template <> struct static_assert_helper<true> {};
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
enum { dummy = sizeof(static_assert_helper<sizeof(int) == 4>)};
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
or somewhat brutish without templates, in any function:
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
\begin_inset listings
|
|
inline false
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
const int d = sizeof(int) - 4;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
switch(0) {
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
case 0:
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
case !(d*d):
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
break;
|
|
\end_layout
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
}
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
|
|
\end_layout
|
|
|
|
\begin_layout Standard
|
|
Any of them in a .cpp file will break compilation as soon as sizeof(int)
|
|
is not equal 4.
|
|
Personally I prefer something like the third version (or the first, if
|
|
using C++0x is allowed).
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
And one of mine: (vfr)
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Itemize
|
|
On dynamics_casts
|
|
\begin_inset Flex URL
|
|
status open
|
|
|
|
\begin_layout Plain Layout
|
|
|
|
http://www.lyx.org/trac/changeset/35855
|
|
\end_layout
|
|
|
|
\end_inset
|
|
|
|
:
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Standard
|
|
A dynamic_cast is necessary when:
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
the object to be casted is from an external library because we can't add
|
|
Qxxx::asXxxx() to Qt e.g.:
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Itemize
|
|
QAbstractListModel to GuiIdListModel,
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
QValidator to PathValidator,
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
QWidget to TabWorkArea,
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
QWidget to GuiWorkArea;
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
the object is to be casted from an interface to the implementing class,
|
|
because the Interface does not know by whom it is implemented:
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Itemize
|
|
ProgressInterface to GuiProgress,
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
Application to GuiApplication.
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Standard
|
|
A dynamic_cast can be replaced by:
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
already existing as***Inset() functions, e.g.:
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Itemize
|
|
asHullInset(),
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
asInsetMath()->asMacro(),
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
asInsetText();
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\begin_layout Itemize
|
|
A static_cast when we are sure this can't go wrong, e.g.:
|
|
\end_layout
|
|
|
|
\begin_deeper
|
|
\begin_layout Itemize
|
|
we are sure that CellData::inset->clone() is an InsetTableCell,
|
|
\end_layout
|
|
|
|
\begin_layout Itemize
|
|
in cases where we explicitly check it->lyxCode().
|
|
\end_layout
|
|
|
|
\end_deeper
|
|
\end_deeper
|
|
\end_deeper
|
|
\begin_layout Bibliography
|
|
\labelwidthstring Bibliography
|
|
\begin_inset CommandInset bibitem
|
|
LatexCommand bibitem
|
|
key "key-1"
|
|
|
|
\end_inset
|
|
|
|
S.
|
|
Meyers.
|
|
Effective C++, 50 Specific Ways to Improve Your Programs and Design.
|
|
Addison-Wesley, 1992
|
|
\end_layout
|
|
|
|
\begin_layout Bibliography
|
|
\labelwidthstring Bibliography
|
|
\begin_inset CommandInset bibitem
|
|
LatexCommand bibitem
|
|
key "key-2"
|
|
|
|
\end_inset
|
|
|
|
Sutter, Herb.
|
|
Exceptional C++: 47 engineering puzzles, programming problems, and solutions.
|
|
ISBN 0-201-61562-2
|
|
\end_layout
|
|
|
|
\begin_layout Bibliography
|
|
\labelwidthstring Bibliography
|
|
\begin_inset CommandInset bibitem
|
|
LatexCommand bibitem
|
|
key "key-4"
|
|
|
|
\end_inset
|
|
|
|
Scott Meyers, C/C++ User's Journal (Vol.18,No.2)
|
|
\end_layout
|
|
|
|
\end_body
|
|
\end_document
|