Skip to content
Mark Giraud edited this page May 28, 2021 · 4 revisions

General

  1. Avoid the use of Magic Numbers. For example use UA_STATUSCODE_BADINTERNALERROR instead of 0x80020000
  2. Avoid using global variables. If you for some reason really need to use global variables, prefix them with g_ so that it is immediately clear in the code, that we are dealing with a global variable.
  3. Almost all of the code is indented by four (4) spaces and regardless of which is better, spaces or tabs, you should indent with four spaces in order to be consistent.
  4. If you find yourself copy-pasting code, consider refactoring it into a function and calling that. Try keeping functions short (about 20-30 lines, that way they will in most cases fit onto the screen). In case of simple switch statements for example this can be exceeded, but there is not much complexity that needs to be understood. Most of the time, if functions are longer than 30 lines, they can be refactored into smaller functions that make their intent clear.
  5. Use of comments

Use C-style comments as follows:

/* Lorem ipsum dolor sit amet */

/* Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
 * eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
 * voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita
 * kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem
 * ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
 * tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
 * At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd
 * gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. */

Note that public header files (in the /include directory) use comments beginning with a double-star to generate documentation. Just follow along the formatting that is already in place.

/** This is documentation */

Every (good) editor can reflow comments for nice formatting.

Do not use C++ comments (double-slash //).

See the following general rules for comments: [1]

Comments are good, but there is also a danger of over-commenting. NEVER try to explain HOW your code works in a comment: it's much better to write the code so that the working is obvious, and it's a waste of time to explain badly written code.

Generally, you want your comments to tell WHAT your code does, not HOW. Also, try to avoid putting comments inside a function body: if the function is so complex that you need to separately comment parts of it, you should probably go back to chapter 6 for a while. You can make small comments to note or warn about something particularly clever (or ugly), but try to avoid excess. Instead, put the comments at the head of the function, telling people what it does, and possibly WHY it does it.

1 Packages:

  1. "Package" = organizational element to aggregate methods, types, constants.
  2. Package are underline-aggregated or CamelCased, lowercase and in singular formal

examples: services_view, encoder, linkedList 3. Pseudo-Packages can be used for consolidation reasons (e.g. enclose dozens built-in types) Pseudo-Packages' name should be using plural to focus on the consolidation aspect

examples: basictypes to aggregate all the hand coded basic types

2 Source file organization:

  1. every file starts with a prefix ua_ followed by the package name examples: ua_encoder.c, ua_transport_binary.c, ua_basictypes.c
  2. source files are located in the /src folder
  3. source files can be further aggreagated on the file-system level, i.e.
	/src/.
		ua_encoder.c
		...	
		util/.
			ua_indexedList.c
			...
NOTE: file-system organization *is not* reflected in the source code

3 Header files

  1. The header files for the public API are stored in the /include folder and have the same name as .c files
  2. The /include folder is a flattened /src folder e.g. it does not contain subdirectories

4 Methods and instances

  1. Methods and instances are prefixed with UA_packageName
  2. Methods and instances are camelCased starting with lower case Examples: UA_encoder_encode, UA_Int32_calculateSize
  3. Use const on variables that will not be changed. For example in functions, make all parameters const, that are not used as out arguments. When declaring local variables in a function make them const, if you know that they will not be modified. When using pointers, remember to declare both the pointer and the actual type as const, if they are never modified: const int *const myInt
  4. In- variables should be always in front of the out- variables in argument lists

5 Types

  1. Types are prefixed with UA_packageName.

  2. Types are CamelCased starting with upper case. Example: UA_ResponseHeader

  3. Builtin datatypes do not have a package name. Example: UA_Int32, UA_Int64

6 Constants, enums, defines

  1. Constants and defines are in full capital letters, eg.: MY_CONST
  2. Constants should be defined in a matching package's .h file, if they are part of the interface. Otherwise they should be kept in the .c file in order to not pollute the interface.
  3. Enums are preferred when defining several related constants, for example the different states of a secure channel:
typedef enum {
    UA_SECURECHANNELSTATE_FRESH,
    UA_SECURECHANNELSTATE_OPEN,
    UA_SECURECHANNELSTATE_CLOSED
} UA_SecureChannelState;

7 Variable naming

  1. Variables should be descriptive and use camelCase starting with a lower case letter.
  2. Some commonly known abbreviations or variable name conventions can be used. This includes for example loop variables like i and temporary variables like tmp. Use common sense, and think about if you would still understand your own code if you looked at it two weeks in the future.
  3. Do not use Hungarian notation. The code base so far does not use it and neither should you, to be consistent. For some reasons, why Hungarian notation might be bad, see here

8 Formatting

  1. Braces: Opening braces start on the same line as a function, if statement, etc. and the closing brace is placed on a separate line. If the next keyword after a closing braces logically belongs to the previous keyword (i.e. if and else), the keyword is placed next to the closing brace on the same line with one space in between. Example:
    if(a) {
        doSomething();
    } else if(b) {
        doSomething();
    } else doSomething();
    
  2. Switch statement: Do not indent the case keyword and place the content of each case in new lines that are indented. If using a code block to limit the scope of variables, follow the above convention. Example:
    switch(a) {
    case 0:
        doA();
    case 1: {
        doB();
        doC();
    }
    }
    
  3. Align multiline expressions. Example:
    int a = 1 +
            2 +
            3;
    
    int b = ((1) + 2) -
            (4 * 5 / 6 % 7);
    
  4. Keep the number of blank lines to a minimum, but leave some if it makes the code more readable.
  5. Surround binary operators with spaces except for member access operators. Do not use spaces around unary operators.
  6. Do not place a space between a keyword and following parentheses.
  7. Do not place spaces within parentheses.
  8. Place a space before, but not after a pointer symbol (*). Example: const int *const **const myPtr;

If you are using CLion you can configure your IDE by importing this scheme.

9 Tests

  1. tests are contained in /tests dir and start with check_ prefix followed by a package name (corresponding .c file)

    example: check_encoder.c, check_securityLayer.c

  2. check_main.c file is used to aggregate test cases that are executed

Still unsure?

If any questions arise concerning code style, feel free to start an issue.