Coding Guidelines

Started by
12 comments, last by 0r0d 6 years, 7 months ago

After a feeled million hours of coding in the past 16 years there have been many ways to write code in many different languages. Some seemed correct to the time they were used, some seemed to be too strict or too chaotic and I also evolved my coding style with each new line written. Now considering the results of over 5 years in professionall game development, tools and engine code as hobbyist and on small and large commercial projects up to AAA titles, there are still many ways one could write code in different languages but also in the same language on different projects in one and the same but also different companies. I mostly agree with; see some trends in C#, C++ coding guidelines that are fully worth to go for but the major difference is on the naming conventions.

Because I have currently to write my own coding guidelines (not for a special project but primary as a personal convention to refer to when coding) and seek for a way I'm happy with, I did some research on different guidelines and came up with following references:

When Epic Games write about Unreal

Quote

Naming Conventions

  • The first letter of each word in a name (e.g. type or variable) is capitalized, and there is usually no underscore between words. For example, Health and UPrimitiveComponent, but not lastMouseCoordinates or delta_coordinates.

  • Type names are prefixed with an additional upper-case letter to distinguish them from variable names. For example, FSkin is a type name, and Skin is an instance of a FSkin.

    • Template classes are prefixed by T.

    • Classes that inherit from UObject are prefixed by U.

    • Classes that inherit from AActor are prefixed by A.

    • Classes that inherit from SWidget are prefixed by S.

    • Classes that are abstract interfaces are prefixed by I.

    • Enums are prefixed by E.

    • Boolean variables must be prefixed by b (e.g. "bPendingDestruction", or "bHasFadedIn").

    • Most other classes are prefixed by F, though some subsystems use other letters.

    • Typedefs should be prefixed by whatever is appropriate for that type: F if it's a typedef of a struct, U if it's a typedef of a UObject etc.

      • A typedef of a particular template instantiation is no longer a template and should be prefixed accordingly, e.g.:

        typedef TArray<FMyType> FArrayOfMyTypes;

This seems a bit confusing when seeking for some type like Animation or Skin (that are both different prefixed with A and F) but prevents various naming conflicts to types and variables when writing a function that accepts FSkin Skin as parameter for example.

Googles c++ guidelines point into a completely different direction when they write

Quote

Type Names

Type names start with a capital letter and have a capital letter for each new word, with no underscores: MyExcitingClass, MyExcitingEnum.

The names of all types — classes, structs, type aliases, enums, and type template parameters — have the same naming convention. Type names should start with a capital letter and have a capital letter for each new word. No underscores. For example:



// classes and structs
class UrlTable { ...
class UrlTableTester { ...
struct UrlTableProperties { ...

// typedefs
typedef hash_map<UrlTableProperties *, string> PropertiesMap;

// using aliases
using PropertiesMap = hash_map<UrlTableProperties *, string>;

// enums
enum UrlTableErrors { ...

Variable Names

The names of variables (including function parameters) and data members are all lowercase, with underscores between words. Data members of classes (but not structs) additionally have trailing underscores. For instance: a_local_variable, a_struct_data_member, a_class_data_member_.

Common Variable names

For example:



string table_name;  // OK - uses underscore.
string tablename;   // OK - all lowercase.


string tableName;   // Bad - mixed case.

Class Data Members

Data members of classes, both static and non-static, are named like ordinary nonmember variables, but with a trailing underscore.



class TableInfo {
  ...
 private:
  string table_name_;  // OK - underscore at end.
  string tablename_;   // OK.
  static Pool<TableInfo>* pool_;  // OK.
};

Struct Data Members

Data members of structs, both static and non-static, are named like ordinary nonmember variables. They do not have the trailing underscores that data members in classes have.



struct UrlTableProperties {
  string name;
  int num_entries;
  static Pool<UrlTableProperties>* pool;
};

See Structs vs. Classes for a discussion of when to use a struct versus a class.

Constant Names

Variables declared constexpr or const, and whose value is fixed for the duration of the program, are named with a leading "k" followed by mixed case. For example:



const int kDaysInAWeek = 7;

All such variables with static storage duration (i.e. statics and globals, see Storage Duration for details) should be named this way. This convention is optional for variables of other storage classes, e.g. automatic variables, otherwise the usual variable naming rules apply.

 

Function Names

Regular functions have mixed case; accessors and mutators may be named like variables.

Ordinarily, functions should start with a capital letter and have a capital letter for each new word (a.k.a. "Camel Case" or "Pascal case"). Such names should not have underscores. Prefer to capitalize acronyms as single words (i.e. StartRpc(), not StartRPC()).



AddTableEntry()
DeleteUrl()
OpenFileOrDie()

(The same naming rule applies to class- and namespace-scope constants that are exposed as part of an API and that are intended to look like functions, because the fact that they're objects rather than functions is an unimportant implementation detail.)

Accessors and mutators (get and set functions) may be named like variables. These often correspond to actual member variables, but this is not required. For example, int count() and void set_count(int count).

Namespace Names

Namespace names are all lower-case. Top-level namespace names are based on the project name . Avoid collisions between nested namespaces and well-known top-level namespaces.

The name of a top-level namespace should usually be the name of the project or team whose code is contained in that namespace. The code in that namespace should usually be in a directory whose basename matches the namespace name (or subdirectories thereof).

Keep in mind that the rule against abbreviated names applies to namespaces just as much as variable names. Code inside the namespace seldom needs to mention the namespace name, so there's usually no particular need for abbreviation anyway.

Avoid nested namespaces that match well-known top-level namespaces. Collisions between namespace names can lead to surprising build breaks because of name lookup rules. In particular, do not create any nested std namespaces. Prefer unique project identifiers (websearch::index, websearch::index_util) over collision-prone names like websearch::util.

For internal namespaces, be wary of other code being added to the same internal namespace causing a collision (internal helpers within a team tend to be related and may lead to collisions). In such a situation, using the filename to make a unique internal name is helpful (websearch::index::frobber_internal for use in frobber.h)

Enumerator Names

Enumerators (for both scoped and unscoped enums) should be named either like constants or like macros: either kEnumName or ENUM_NAME.

Preferably, the individual enumerators should be named like constants. However, it is also acceptable to name them like macros. The enumeration name, UrlTableErrors (and AlternateUrlTableErrors), is a type, and therefore mixed case.



enum UrlTableErrors {
  kOK = 0,
  kErrorOutOfMemory,
  kErrorMalformedInput,
};
enum AlternateUrlTableErrors {
  OK = 0,
  OUT_OF_MEMORY = 1,
  MALFORMED_INPUT = 2,
};

Until January 2009, the style was to name enum values like macros. This caused problems with name collisions between enum values and macros. Hence, the change to prefer constant-style naming was put in place. New code should prefer constant-style naming if possible. However, there is no reason to change old code to use constant-style names, unless the old names are actually causing a compile-time problem.

Macro Names

You're not really going to define a macro, are you? If you do, they're like this: MY_MACRO_THAT_SCARES_SMALL_CHILDREN.

Please see the description of macros; in general macros should not be used. However, if they are absolutely needed, then they should be named with all capitals and underscores.



#define ROUND(x) ...
#define PI_ROUNDED 3.0

Exceptions to Naming Rules

If you are naming something that is analogous to an existing C or C++ entity then you can follow the existing naming convention scheme.

bigopen() function name, follows form of open() uint typedef bigpos struct or class, follows form of pos sparse_hash_map STL-like entity; follows STL naming conventions LONGLONG_MAX a constant, as in INT_MAX

So they heavily make use of typos, underscores and also lower case prefixes to identify different kinds of member, static, nonstatic and function names and in the same breath except there rules for various special cases.

Some other examples from different projexts I was invovled to also use and do not use prefixing types or use underscores


class Class
{
    const <type> cConstant;
    const <type> Constant;
    const <type> __Constant;
  
    <type> _myClassMember;
    <type> _MyClassMember;
    <type> myClassmember;
    <type> mMyClassMember;
  
    <type> function(<type> parameter);
    <type> Function(<type> parameter);
    <type> Function(<type> aParameter);
}
class NAClass //avoid using namespaces, instead prefix anything with a 2 letter namespace like identifier
{
    ... 
}

Dont need to mention that Visual Studio will raise a Warning/Exception that a type is named as same as a function parameter when using a class


class Container
{
    private int size; //current size
    public Resize(int size) //will cause compiler telling that type matches a member type
    {
        //do resize here
    }
}

So in the end anyone does he or she thinks that it is worth to be done and so me do too. I would like to hear your opinions to why and what codings style do you prefer or are involved to in whatever way. What do you think makes a good standard especially for the most common point, Naming Convetions?

Will be corious to read your opinions

Advertisement

In the end it all comes down to your preferences, since the compiler wont care at all.  But generally, I think the important things are that whatever you choose is consistent throughout the code and creates as little confusion or obfuscation as possible.  Other things like typing speed can also come into play, but again that can be a matter of preference especially with auto-complete and other built-in features that may or may not exist in your code editor of choice.

Personally I use camelcase, avoiding underscores whenever possible, using them only in special cases.  I start my classes and function with upper case letters, local variables with lower case, and member variables start with "m" followed by capital letter for variable name.  Macros are all caps separated by underscores.  For curly braces I use Allman style because it aligns the curly braces and makes them easier to match up when looking at code, as well as separates the block of code inside the braces from the code before it... which again makes it easier to read for me.  I have trouble telling what code does if it's all bunched up into a solid brick of text.

16 minutes ago, 0r0d said:

since the compiler wont care at all

There are a few things one should avoid, though -- one of which is listed as one of the last examples in the original post.

Identifiers should not start with leading underscores, nor should they contain double underscores. These can run into issues with reserved names.

 

Other than that I agree that's it's just a preference thing. If you're working with someone else, come to an agreement. The fact that there exists so many and different guidelines is good evidence for there not being any One True Style™.

Hello to all my stalkers.

I mostly agree with the C# Style 0r0d mentioned except that in my opinion the leading m/a/c prefix for class/argument/constant types could lead to faster recognition what a name is used for but I personally feel that it leads to more thinking about "was this a member, argument or temporary I used here" rather than writing for the initial intention.

For example I know i need the size stored in my class right now so I would in some kind of subconscious auto typing write "size" and go ahead in the knowing of this beeing the correct addressor I intendet. Using a prefix "m" for my class member "mSize" could (or potentially would most of the time in my case) lead to a less subconscious typing but a conscious active reminder about "do not forgett that it isnt size rather than mSize" that might lead to a break in code flow.

When I had C# first and learned C++ later, there were no such things like prefixing your member/argument/constant whatever variables in my years of school at all.

Prefixing type is generally a terrible idea.

Scope decoration, OTOH, can be kinda useful. Using m or _ for member variables, for example, allows differentiation between parameters and members, and also intellisense will quickly list all your members for that class. Sure, you could explicitly type "this->", but that's overkill.

I'm also open to prefixing pointers with p, purely because it makes your life a tiny bit easy when you're coding (variable. vs pVariable->).

But basically, pick a standard with your team, agree on it (quickly) and stick to it.

 

Readability is key.

if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight
11 minutes ago, ChaosEngine said:

Prefixing type is generally a terrible idea.

How so?

3 hours ago, 0r0d said:

How so?

CBecause GProgramming VIs Aa NWork POf NLiterature.  PIt VMakes PIt AVery VDifficult PFor NHumans VATo VRead CAnd AThe NCompiler PDoesn't VCare.

CAlso, IWhat's PWith CAll AThe GCapitalization PIn ASo AMany NGuidelines?

Stephen M. Webb
Professional Free Software Developer

5 hours ago, 0r0d said:

How so?

Basically what @Bregma said.

All it does is add noise. It also discourages refactoring (int iThing = GetAThing() now returns a float, now we have rename every variable).

The whole lpszThisIsAString was a mistake in the first place. Initially, "Hungarian" notation was meant to encode metadata in the variable name. For example, wndX vs scnX to denote a window coordinate versus a screen coordinate. The idea was that if you see


wndX = scnX // oops should be wndX = ScreenToWindow(scnX)

it would look immediately wrong. 

But even that is no longer that useful (you could make different ScreenCoord and WindowCoord types, for example).

 

 

if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight
4 hours ago, Bregma said:

CBecause GProgramming VIs Aa NWork POf NLiterature.  PIt VMakes PIt AVery VDifficult PFor NHumans VATo VRead CAnd AThe NCompiler PDoesn't VCare.

CAlso, IWhat's PWith CAll AThe GCapitalization PIn ASo AMany NGuidelines?

Oh, you were talking about prefixes for the type of a variable... that wasnt clear to me when I originally read your post.

Yeah I agree it's not a thing I would do.

I thought you were talking about prefixes for things like classes or functions, as in instead of namespaces.

i.e.:

rendTexture vs rend::Texture

or

rendGetTexture() vs rend::GetTexture()

But is using mMember, aArgument and so on not the same as prefixing stuff; where


mWndPos = aWndPos + cWndPos;
//do some stuff to mWndPos;

*pWndPos = mWndPos;

is as same confusing as whatever Bregman wrote?

I know that it would make life easier when using some kind of member type to variable separation but writing this-> seems to be pretty standard here

This topic is closed to new replies.

Advertisement