Fandom

PvPGN

Coding Style

108pages on
this wiki
Add New Page
Talk0 Share

Because PvPGN is developed mainly on POSIX/Linux systems there are some things specific to this type of platform (like the line ending of all text/source files to be "Unix-style").

One thing which is overlooked by newbie coders is the "aesthetical" side of the code. It may not be so important to many people (which code on the idea "if it works then its good") but for us, coding on PvPGN is VERY important. When you are coding for PvPGN, PLEASE try to make your code look similar to already written code (this includes indenting, identifier names, etc...). Keeping the code look "the same" makes its reading a lot more easier so, finding bugs easier so coding better.

Other overlooked aspect for newbie coders is code replication. Please DON'T copy and paste code around !!! If you need to copy common functionality from some place, think about making some API of that functionality, put it in some classes and use it from both places. I repeat, DON'T replicate code.

When allocating memory (or some other kind of resource like a file, etc) inside a function always free it (release the resource) in the same function before its exit (exceptions: the function returns the allocated memory/resource in which case the calling function should take care of the allocated memory; or the allocated memory is cached/stored somewhere to be used later, in which case you must make sure it will be free()d when not needed anymore).

In the start-up code of any "external" function (function which may be called from other modules then the one containing it or a public method ) please check ALL the input parameters (you will notice how PvPGN already does that in such functions). In case of errors detected in such cases (and not only) code should "throw" an exception (see section 5.d).

When developing code you should compile always with "all warnings" enabled and try to fix them (some warnings uncover real bugs), like for gcc use "-Wall". When running/testing the codes you should use a memory debugger like valgrind. For more details about how to run pvpgn check Coding Tools.

To allocate/free memory use ONLY new/delete calls (this makes sure that out of memory condition is detected and nicely handled because in that case "new" automatically "throws" std::bad_alloc exception).

Indenting Edit

PvPGN indentation should be done ONLY with "tabs" (exception see below), the tab size is 8 "spaces".

We indent a level after if, while, do-while, for, case (but NOT from switch), class/struct/union (but NOT the access modifier public/protected/private) and a starting brace. The position of the starting brace relative to the keywords from above is at the same level as the keyword.

We except to indent after switch, namespace braces and the access modifiers because otherwise things will look "too indented" and make codes less readable.

Functions (methods/constructors) and their parameters Edit

Function definitions should have the type returned by the function on it's own line than the function name and it's parameter list. Like in:

const std::string&
MyClass::getString()
{
}

Function parameters should be passed by reference for "aggregate" types (like class/struct/union). This reference usually is const, ie "const Type& param". Accessors should return references to aggregate members (this is safe because we presume the object's life time exceeds the reference cacher's use time, if not the calling codes can just cache a copy of the object).

Functions should never have their bodies in the .h file (exception, class and function templates that NEED to be in the .h file completely).

Exceptions and error handling Edit

"Errors are not always exceptions". Keep this in mind!

But generally, errors are exceptions. In the common sense and in turn, in the codes. In case of errors (that is errors that shouldnt happen too often, not common case errors) the codes should throw an exception. Exceptions allows for a very clean and versatile way to detach the place of error detection to the place of error handling (in C you detect an error in some low-level codes and you report it in the return value and this is checked and propagated by checks to the higher level codes that will probably handle the error). Also with C++ exceptions the "normal" code flow it's NOT disrupted and polluted by trying to handle errors everywhere you might hit one. Exceptions basically offer an alternative SLOW code path. It is slow (much slower than normal code flow) and this is why it should be used to signal relatively "rare" events (thus called exceptions) and not something "common".

But using exceptions you might be careful with them to not "leak" resources. The problem is that if you allocate some resource in some codes and then you might get an exception (not even from your codes but indirectly from codes you call) you need to make sure the resources acquired are released BEFORE the exception propagates out of your code scope. This is _easily_ done with C++ and the RAII principle (http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization). The rule is simple, ALL resource acquisitions should be done using some kind of wrappers that release the resource in their destructor. This also makes codes a lot more simple when exiting code scope on non-error cases because you do NOT need to explicitly release the "wrapped" resource as it will release automatically when leaving the code scope.

You are allowed ONLY to throw objects, NEVER throw basic types. All the exceptions you throw HAVE to be derived from std::exception (thus providing the .what() method to get a C-string of the description of the error). It is best however, you derivate from std::runtime_error & co. You MAY create your own exception class (with the limitations from above) IF AND ONLY IF your exception represents an error that NEEDS to be handled differently (ie catch separately from other exceptions). All such user defined exception classes should be defined NESTED in the class that throws them.

Catching exceptions should ONLY be done using the "const reference" catching scheme like in:

} catch (const std::exception& ex) {

This allows for 2 important things to happen:

  • it allows proper polymorphic mechanisms for exceptions (so with the example above ANY exception derived from std::exception is caught and handled as it should be)
  • it behaves nicely on out of memory condition ie an ISO C++98 compliant compiler even when out of memory condition is reached has to provide ENOUGH memory to create a temporary object that (and this is what you do when throwing an exception to be caught with reference, you throw like throw MyException(); whcih means it creates a temporary object MyException() and that object reference is passed to the handlers)

Namespaces Edit

All pvpgn codes will belong to the "pvpgn" namespace. Specific application codes will belong to a namespace inside "pvpgn" like "pvpgn::bnetd". Common codes need to still be just in "pvpgn" so all other codes will "see" them.

Never ever use a "using" keyword inside a .h file. Inside .cpp files you _may_ use them.

Some types, classes, etc are better to be declared inside some other class (like the exceptions, see the above section or smart pointers for the class).

"static" keyword to declare "local" variables and functions it's not allowed. Instead of it, use unnamed namespaces. A ISO C++98 compliant compiler has to provide for each separate translation unit an unique name for the unnamed namespace that could be declared in that translation unit. As such, instead of

static int myvar;
static int myfunc(int);

extern int externfunc()
{
}

static int myfunc()
{
}

Make it

namespace
{

int myvar;
int myfunc(int);

}

extern int externfunc()
{
}

namespace
{

int myfunc(int)
{
}

}


Identifiers Edit

Class names should start with a capital and all words composing it should too (Java-like style, like in "MyStuff"). All methods should start with a lowercase letter and then all words composing it with uppercase. Access methods should start with get/set and then the name of the member accessed with the case rules already mentioned.

Include order Edit

In .cpp files the first include should be "common/setup_before.h" followed by the corresponding header of the cpp file (ie in clan.cpp after setup_before.h you include clan.h).

Then you include (this applies to headers too not only .cpp now) the headers of standard C++ lib, then the headers from C90 as offered by standard C++ (ex. <cstring> etc), then the POSIX/win32/system dependent headers.

Code Flow Edit

ExpandIcon
This article or section needs expanding. This just needs to be completed

You can find more information on what exactly needs doing in the discussion for this article. Remember to use the Manual of Style and to remove this template once the article has been expanded.


<to be written, not sure how it will be after the complete C++ conversion>

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.