This document intends to establish a common coding style for the C++ projects of the EPITA students.
Covered topics:
The specifications in this document are to be known in detail by all students.
All submitted projects must comply exactly with these rules.
WARNING: Despite the fact that this document looks pretty much like that of the C Coding Style, it is different and specifies different rules. Read it carefully.
Some sections of this document are identical to the corresponding ones of the C Coding Style. In this case, they will not be repeated and you are asked to refer to the C Coding Style guidelines.
The rules specified in this document give you much more freedom than the C Coding Style one, use this freedom wisely.
The Tiger Compiler Assignment also has a section about Coding Style, you MUST follow it for TC. If the Tiger Assignment contradicts this document, then it is authoritative for the Tiger Compiler.
If you are a beginner in C++, you are strongly suggested to entirely read The C++ FAQ light of comp.lang.c++.
--- The Detailed Node Listing ---
How to read this document
Naming conventions
Preprocessor-level specifications
Writing style
Object Oriented considerations
Global specifications
Project layout
Differences with the C Coding Style
Index and Table of Contents
This document adopts some conventions described in the following nodes.
These guidelines use the words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY and OPTIONAL as described in RFC 2119.
Here are some reminders from RFC 2119:
Do not mix up the intention and extension of this document.
The intention is to limit obfuscation abilities of certain students with prior C++ experience, and make uniform the coding style of all students, so that group work does not suffer from style incompatibilities.
The extension, that is, the precision of each “rule”, is there to explain how the automated coding style verification tools operate.
In brief, use your common sense and understand the intention, before complaining about the excessive limitations of the extension.
Examples in these guidelines are there for illustratory purposes only. When an example contradicts a specification, the specification is authoritative.
Be warned.
As a side-note, do not be tempted to “infer” specifications from the examples presented.
Names in programs must comply to several rules. They are described in the following nodes :
The recommendations provided by the C Coding Style guidelines also apply in C++: give your {files,variables,classes,namespaces,etc} relevant (English) names.
Additionally, the following rules apply:
LikeThis
.
Class should be named in mixed case; for instance Exp
,
StringExp
, TempMap
, InterferenceGraph
etc. This
also applies to class templates.
public
members like_this
. No upper case
letters, and words are separated by an underscore.
private
and protected
members
like_this_
. It is extremely convenient to have a special
convention for private and
protected members: you make it clear to the reader, you avoid gratuitous
warnings about conflicts in constructors, you leave the “beautiful”
name available for public members etc. We used to write
_like_this
, but this goes against the standard (see Stay out of reserved names).
For instance, write:
class IntPair { public: IntPair (int first, int second) : first_ (first), second_ (second) { } protected: int first_, second_; }
LikeThis
per files
like-this.*. Each class LikeThis
is implemented in a
single set of file named like-this.*. Note that the mixed case
class names are mapped onto lower case words separated by dashes.
There can be exceptions, for instance auxiliary classes used in a single place do not need a dedicated set of files.
likethis
.
This section is incomplete and you must follow the guidelines specified in the section Name Conventions of Tiger Assignment.
Preprocessor macro names MUST be entirely capitalized.
When declaring types, type names MUST be suffixed with _type
typedef typename MyTraits<T>::res value_type; typedef std::map<const Symbol, Entry_T> map_type; typedef std::list<map_type> symtab_type;
Rationale: this is a common idiom in C++ (which happens to be used by the stl)
Rationale: for not using _t
: identifiers ending with
_t
are reserved by posix (beside others).
The global layout of files, and sections of code pertaining to the C preprocessor (which happens to be used in C++), including file inclusion and inclusion protection, must comply to specifications detailed in the following sections.
#ifndef DEV_BSIZE # ifdef BSIZE # define DEV_BSIZE BSIZE # else /* !BSIZE */ # define DEV_BSIZE 4096 # endif /* BSIZE */ #endif /* !DEV_BSIZE */
in\ t ma\ in () { }
is strongly forbidden.
Rationale: macros should not allow for hidden syntactic “effects”.
break
, continue
, return
or throw
.
Don't write:
/// Declaration of the Foo class. class Foo { ... };
It is so obvious that you're documenting the class and the constructor that you should not write it down. Instead of documenting the kind of an entity (class, function, namespace, destructor...), document its goal.
/// \brief Swap the reference with another. /// The method swaps the two references and returns the first. ref& swap (ref& other);
write:
/// @brief Swap the reference with another. /// Swap the two references and return the first. ref& swap (ref& other);
The same rules apply to ChangeLogs.
Rationale: The clients of your interfaces will read your headers to know how your library or module works. They don't want to dig in the implementation to find the documentation.
Rationale: The code is much more read that written and you should favor the (many) readers instead of the (often only) implementer.
Rationale: Doing so leaves you some room to comment your implementation.
using namespace
in a header.
The following sections specify various aspects of what constitutes good programming behavior at the lexical level.
This is wrong:
if (x == 3) { x += 4; } | This is correct:
if (x == 3) { x += 4; } |
namespace foo { // FIXME: Add 500 lines here. } // namespace foo
But SHOULD NOT state the obvious.
try
or a do
on the same line. This code is correct:
int foo () { int c = 0; int r = 0; do { try { r = bar (++c); } catch (...) { r = 0; } } while (c < 42 && !r); return r; }
Pointers and references are part of the type, and MUST be put near the type, not near the variable.
const char* p; // not `const char *p;' std::string& s; // not `std::string &s;' void* magic (); // not `void *magic();'
Hint: Your compiler can help you to detect uninitialized local variables.
This is wrong:
x = 3; y = 4; | This is correct:
x = 3; y = 4; |
goto
statement MUST NOT be used.
Of course, this rule is not applicable on source files generated by external tools (like bison or flex).
asm
declaration MUST NOT be used.
.
, ->
, ::
, operator[]
and
operator()
operators MUST NOT be padded.
This is wrong:
x+=10*++x; y=a?b:c; | This is correct:
x += 10 * ++x; y = a ? b : c; |
0
instead of NULL
for NULL pointers.
int foo (int n) { return bar () + n; }
std::cout << "Hello!" << std::endl << "World!\n" << std::endl;
do if typeid sizeof case catch switch template for throw while try
This is wrong:
if(x == 3) foo3(); |
This is correct:
if (x == 3) foo3 (); |
template<>
for explicit
specializations.
template<> class Foo<int> { ... };
for
construct MAY be empty.
Note that more often than not, the while
construct better
represents the loop resulting from a for
with an empty initial
part.
These are wrong:
for (;;) ; for ( ; ; ) ; | This is correct:
for (;;) ; |
This is wrong:
for (len = 0; *str; ++len, ++str); |
These are correct:
for (len = 0; *str; ++len, ++str) ; |
Rationale: although this whitespace is usually not visible, it clobbers source code with useless bytes.
When declaring a class, start with public
members, then
protected
, and last private
members. Inside these groups,
you are invited to group by category, i.e., methods, types, and members
that are related should be grouped together.
Rationale: People reading your class are interested in its interface
(that is, its public
part). private
members should not
even be visible in the class declaration (but of course, it is mandatory
that they be there for the compiler), and therefore they should be
“hidden” from the reader.
This is an example of what should not be done:
class Foo { public: Foo (std::string, int); virtual ~Foo (); private: typedef std::string string_type; public: std::string bar_get () const; void bar_set (std::string); private: string_type bar_; public: int baz_get () const; void baz_set (int); private: int baz_; }
instead, write:
class Foo { public: Foo (std::string, int); virtual ~Foo (); std::string bar_get () const; void bar_set (std::string); int baz_get () const; void baz_set (int); private: typedef std::string string_type; string_type bar_; int baz_; }
and add useful Doxygen comments.
class Derived: public Base { // ... }; /// Object function to compare two Temp*. struct temp_ptr_less: public std::binary_function<const Temp*, const Temp*, bool> { bool operator() (const Temp* s1, const Temp* s2) const; };
virtual
in subclass declarations.
If a method was once declared virtual
, it remains virtual.
Nevertheless, as an extra bit of documentation to your fellow
developers, repeat this virtual
:
class Base { public: // ... virtual foo (); }; class Derived: public Base { public: // ... virtual foo (); };
std::list<int> l; std::pair<std::list<int>, int> p;
with a space after the comma, and of course between two closing >:
std::list<std::list<int> > ls;
These rules apply for casts:
// Come on baby, light my fire. int* p = static_cast<int*> (42);
template <class T1, class T2> struct pair;
with one space separating the keyword template
from the list of
formal parameters. For explicit specializations you MAY (and are
RECOMMENDED to) write template<>
:
template<> struct pair<int, int> { ... };
()
operator is not a list of arguments.
class Foo { public: Foo (); virtual ~Foo (); bool operator() (int n); };
void
if your function or method does not take any
argument. The C++ way of doing is to simply write that your function does not
take any argument.
Don't do this:
int main(void) { return 0; }
Write this instead (without the comments, obviously):
int main () // Not like C: main can't be passed any argument { } // In C++, main implicitly returns 0
int foo (int n) { return bar (n); }
Don't put initializations or constructor invocations on the same line
as the constructor. As a matter of fact, you MUST NOT even leave
the colon on that line. Instead of A::A (): B (), C()
, write
either:
A::A () : B (), C () { }
or
A::A () : B (), C () { }
Rationale: the initialization belongs more to the body of the constructor than its signature. And when dealing with exceptions leaving the colon above would yield a result even worse than the following.
A::A () try : B (), C () { } catch (...) { }
explicit
unless you know you want them to trigger implicit conversions.
Don't write:
class Foo { public: Foo (int i); private: int i_; }; Foo::Foo (int i) { i_ = i; }
But instead write:
class Foo { public: Foo (int i); private: int i_; }; Foo::Foo (int i) : i_ (i) { }
virtual
if there is at least one
virtual method in your class
(see
When should my destructor be virtual?).
operator,
, operator||
and
operator&&
.
operator&
.
friend
s to circumvent bad design.
new
.
See
this FAQ.
throw
because some compilers
(including some versions of gcc) will reject your code. Here is an
example of what should not be done:
int main () { throw (Foo ()); }
Some general considerations about the C++ sources of a project are specified in the following sections.
Use the C++ casts instead: dynamic_cast
, static_cast
,
const_cast
, reinterpret_cast
.
Rationale: good programming behavior includes proper type handling.
reinterpret_cast
is NOT RECOMMENDED.
You SHOULD NOT be using global objects or objects with static storage. Whenever you do, you must be aware that their construction order is subject to the so-called static initialization order fiasco (see this link).
Specifications in this chapter are to be altered (most often relaxed) by the assistants on a per-project basis. When in doubt, follow these guidelines.
This section is identical to the corresponding one described in the C Coding Style.
Note, however, that many people tend to use make tests instead
of make check or to put their tests in a directory named
test
or check
whereas it SHOULD be named tests
in order to comply with the gnu Coding Standards.
This is a non exhaustive list of the differences between the guidelines provided by this document and that provided by the C Coding Style.
s_
, u_
,
e_
, t_
before your type names.
g_
.
/* ** Comment. */
int* p
)
despite the (sad) fact that int* p, q
declares a pointer and an
integer. You MUST NOT declare two things on the same line anyway.
return
value doesn't have to be in parentheses. Actually, it
SHOULD NOT unless adding parentheses makes it easier to read (e.g, because
it spans over more than one line).
else if
on the same line.
_type
: Typedef suffixasm
: Statementsbreak
: Statementscontinue
: Statementsgoto
: Statementsreturn
: Statements