550 lines
20 KiB
Markdown
550 lines
20 KiB
Markdown
|
|
# Contributing to open62541
|
||
|
|
|
||
|
|
Contributions to open62541 include code, documentation, answering user
|
||
|
|
questions, running the project's infrastructure, and advocating for all types of
|
||
|
|
open62541 users.
|
||
|
|
|
||
|
|
The open62541 project welcomes all contributions from anyone willing to work in
|
||
|
|
good faith with other contributors and the community. No contribution is too
|
||
|
|
small and all contributions are valued.
|
||
|
|
|
||
|
|
This guide explains the process for contributing to the open62541 project's core
|
||
|
|
repository and describes what to expect at each step. Thank you for considering
|
||
|
|
these point.
|
||
|
|
|
||
|
|
Your friendly open62541 community!
|
||
|
|
|
||
|
|
## Code of Conduct
|
||
|
|
|
||
|
|
The open62541 project has a [Code of Conduct](./CODE_OF_CONDUCT.md) that *all*
|
||
|
|
contributors are expected to follow. This code describes the *minimum* behavior
|
||
|
|
expectations for all contributors.
|
||
|
|
|
||
|
|
## Pull Requests
|
||
|
|
|
||
|
|
Everybody can propose a pull request (PR). But only the core-maintainers of the
|
||
|
|
project can merge PR.
|
||
|
|
|
||
|
|
The following are the minimal requirements that every PR needs to meet.
|
||
|
|
|
||
|
|
- **Pass Continuous Integration (CI)**: Every PR has to pass our CI. This
|
||
|
|
includes compilation with a range of compilers and for a range of target
|
||
|
|
architectures, passing the unit tests and no detected issues with static code
|
||
|
|
analysis tools.
|
||
|
|
|
||
|
|
- **Code-Style**: Please consider the [Code-Style](#code-style) recommendations
|
||
|
|
when formatting your code.
|
||
|
|
|
||
|
|
- **Signed CLA**: Every contributor must sign the Contributor License Agreement
|
||
|
|
(CLA) before we can merge his first PR. The signing can be done online. The
|
||
|
|
link automatically appears on the page of the first PR. In addition, the CLA
|
||
|
|
text can be accessed [here](https://cla-assistant.io/open62541/open62541).
|
||
|
|
|
||
|
|
- **Separation of Concerns**: Small changes are much easier to review.
|
||
|
|
Typically, small PR are merged much faster. For larger contributions, it might
|
||
|
|
make sense to break them up into a series of PR. For example, a PR with a new
|
||
|
|
feature should not contain other commits with only stylistic improvements to
|
||
|
|
another portion of the code.
|
||
|
|
|
||
|
|
- **Feature Commits**: The same holds true for the individual PR as well. Every
|
||
|
|
commit inside the PR should do one thing only. If many changes have been
|
||
|
|
applied at the same time, `git add --patch` can be used to partially stage and
|
||
|
|
commit changes that belong together.
|
||
|
|
|
||
|
|
- **Commit Messages**: Good commit messages help in understanding changes.
|
||
|
|
See the next section.
|
||
|
|
|
||
|
|
- **Linear Commit History**: Our goal is to maintain a linear commit history
|
||
|
|
where possible. Use the `git rebase` functionality before pushing a PR. Use
|
||
|
|
`git rebase --interactive` to squash bugfix commits.
|
||
|
|
|
||
|
|
These labels can be used for the PR title to indicate its status.
|
||
|
|
|
||
|
|
- `[WIP]`: The PR is work in progress and at this point simply informative.
|
||
|
|
- `[Review]`: The PR is ready from the developers perspective. He requests a
|
||
|
|
review from a core-maintainer.
|
||
|
|
- `[Discussion]`: The PR is a contribution to ongoing technical discussions. The
|
||
|
|
PR may be incomplete and is not intended to be merged before the discussion
|
||
|
|
has concluded.
|
||
|
|
|
||
|
|
The core-maintainers are busy people. If they take especially long to react,
|
||
|
|
feel free to trigger them by additional comments in the PR thread. Again, small
|
||
|
|
PR are much faster to review.
|
||
|
|
|
||
|
|
It is the job of the developer that posts the PR to rebase the PR on the target
|
||
|
|
branch when the two diverge.
|
||
|
|
|
||
|
|
### Changes to the Public API
|
||
|
|
|
||
|
|
The *public* API is the collection of header files in the /include folder.
|
||
|
|
|
||
|
|
Changes to the public API are under especially high scrutiny. Public API changes
|
||
|
|
are best discussed with the core-maintainers early on. Simply to avoid duplicate
|
||
|
|
work when changes to the proposed API become necessary.
|
||
|
|
|
||
|
|
You can create a special issue or PR just for the sake of discussing a proposed
|
||
|
|
API change. The actual implementation can follow later on.
|
||
|
|
|
||
|
|
Breaking changes are frowned upon. If you cannot avoid API changes, try to
|
||
|
|
create a new one and keep the old API alive (maybe with an addition
|
||
|
|
UA_DEPRECATED macro).
|
||
|
|
|
||
|
|
Breaking changes have to be mentioned in the CHANGELOG file.
|
||
|
|
|
||
|
|
## Commit and PR Hygiene
|
||
|
|
|
||
|
|
We have very precise rules over how our git commit messages can be formatted.
|
||
|
|
This leads to **more readable messages** that are easy to follow when looking
|
||
|
|
through the **project history**. But also, we use the git commit messages to
|
||
|
|
**generate the change log**.
|
||
|
|
|
||
|
|
This convention is identical to the [Conventional
|
||
|
|
Commits](https://www.conventionalcommits.org) specification or the one used by
|
||
|
|
Angular.
|
||
|
|
|
||
|
|
Each commit message consists of a **Header**, a **Body** and a **Footer**. The
|
||
|
|
header has a special format that includes a **Type**, a **Scope** and a
|
||
|
|
**Subject**:
|
||
|
|
|
||
|
|
```text
|
||
|
|
<type>(<scope>): <subject>
|
||
|
|
<BLANK LINE>
|
||
|
|
<body>
|
||
|
|
<BLANK LINE>
|
||
|
|
<footer>
|
||
|
|
```
|
||
|
|
|
||
|
|
The **Header** is mandatory and the **Scope** of the header is optional.
|
||
|
|
|
||
|
|
Any line of the commit message cannot be longer 100 characters! This allows the
|
||
|
|
message to be easier to read on GitHub as well as in various git tools.
|
||
|
|
|
||
|
|
The footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
|
||
|
|
|
||
|
|
Samples: (even more [samples](https://github.com/angular/angular/commits/master))
|
||
|
|
|
||
|
|
```text
|
||
|
|
docs(server): add function documentation
|
||
|
|
```
|
||
|
|
```text
|
||
|
|
fix(core): fix parsing of endpoint url
|
||
|
|
|
||
|
|
Parsing of endpoint urls now also supports https
|
||
|
|
```
|
||
|
|
|
||
|
|
If the commit reverts a previous commit, it should begin with `revert: `,
|
||
|
|
followed by the header of the reverted commit. In the body it should say: `This
|
||
|
|
reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||
|
|
|
||
|
|
The commit **Type** Must be one of the following:
|
||
|
|
|
||
|
|
- **build**: Changes that affect the build system or external dependencies
|
||
|
|
- **ci**: Changes to our CI configuration files and scripts (example scopes:
|
||
|
|
travis, appveyor, fuzz)
|
||
|
|
- **docs**: Documentation only changes
|
||
|
|
- **feat**: A new feature
|
||
|
|
- **fix**: A bug fix
|
||
|
|
- **perf**: A code change that improves performance
|
||
|
|
- **refactor**: A code change that neither fixes a bug nor adds a feature
|
||
|
|
- **style**: Changes that do not affect the meaning of the code (white-space,
|
||
|
|
formatting, missing semi-colons, etc)
|
||
|
|
- **test**: Adding missing tests or correcting existing tests
|
||
|
|
|
||
|
|
The commit **Scope** is optional, but recommended to be used. It should be the
|
||
|
|
name of the component which is affected (as perceived by the person reading the
|
||
|
|
changelog generated from commit messages). The following is the list of
|
||
|
|
supported scopes:
|
||
|
|
|
||
|
|
- **arch**: Changes to specific architecture code in `root/arch`
|
||
|
|
- **el**: Changes to the eventloop and associated event sources (also networking)
|
||
|
|
- **client**: Changes only affecting client code
|
||
|
|
- **core**: Core functionality used by the client and server
|
||
|
|
- **ex**: Example code changes
|
||
|
|
- **mt**: Changes specifically for multithreading
|
||
|
|
- **nc**: Nodeset compiler
|
||
|
|
- **pack**: Packaging setting changes
|
||
|
|
- **plugin**: Change to any (optional) plugin
|
||
|
|
- **pubsub**: Changes to the pubsub code
|
||
|
|
- **sec**: Changes to security, encryption, etc.
|
||
|
|
- **server**: Changes only affecting server code
|
||
|
|
|
||
|
|
The **Subject** contains a succinct description of the change:
|
||
|
|
|
||
|
|
- Use the imperative, present tense: "change" not "changed" nor "changes"
|
||
|
|
- Don't capitalize the first letter
|
||
|
|
- No dot (.) at the end
|
||
|
|
|
||
|
|
For the **Body**, Just as in the **Subject**, use the imperative, present tense: "change" not
|
||
|
|
"changed" nor "changes". The body should include the motivation for the change
|
||
|
|
and contrast this with previous behavior.
|
||
|
|
|
||
|
|
The **Footer** should contain any information about **Breaking Changes** and is also
|
||
|
|
the place to reference GitHub issues that this commit **Closes**.
|
||
|
|
|
||
|
|
## Code Style
|
||
|
|
|
||
|
|
### General
|
||
|
|
|
||
|
|
1. Avoid the use of [Magic Numbers](https://en.wikipedia.org/wiki/Magic_number_(programming)#Unnamed_numerical_constants).
|
||
|
|
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:
|
||
|
|
|
||
|
|
```c
|
||
|
|
/* 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.
|
||
|
|
|
||
|
|
```c
|
||
|
|
/** 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\]](https://www.kernel.org/doc/Documentation/CodingStyle)
|
||
|
|
|
||
|
|
> 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.
|
||
|
|
|
||
|
|
### Methods
|
||
|
|
|
||
|
|
1. Public methods are prefixed with `UA_`. If a public method operates on a
|
||
|
|
server it begins with `UA_Server_` and takes a pointer to `UA_Server` as its
|
||
|
|
first arguments (the same for the client).
|
||
|
|
2. Methods and instances are camelCased starting with *lower* case
|
||
|
|
Examples: `UA_Server_new`, `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
|
||
|
|
|
||
|
|
### Types
|
||
|
|
|
||
|
|
1. Public types are prefixed with `UA_`
|
||
|
|
2. Types are CamelCased starting with *upper* case. Example: `UA_ResponseHeader`
|
||
|
|
3. Don't use `var = {0}` to zero-initialize variables. This results in warnings
|
||
|
|
for some exotic environments (e.g. some versions of the Android NDK). Use
|
||
|
|
`memset(ptr, 0, size)` insted.
|
||
|
|
|
||
|
|
### Constants, enums, defines
|
||
|
|
|
||
|
|
1. Constants and defines are in full capital letters, eg.: `UA_CONST`
|
||
|
|
2. 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;
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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](https://stackoverflow.com/questions/111933/why-shouldnt-i-use-hungarian-notation)
|
||
|
|
|
||
|
|
### 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:
|
||
|
|
```c
|
||
|
|
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:
|
||
|
|
```c
|
||
|
|
switch(a) {
|
||
|
|
case 0:
|
||
|
|
doA();
|
||
|
|
case 1: {
|
||
|
|
doB();
|
||
|
|
doC();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
3. Align multiline expressions. Example:
|
||
|
|
```c
|
||
|
|
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](https://gist.github.com/Infinity95/c5c743180b447863aff9e5c76dfde0bb).
|
||
|
|
|
||
|
|
### Tests
|
||
|
|
|
||
|
|
Tests are contained in the `/tests` dir and start with check_ prefix followed by a
|
||
|
|
package name (corresponding .c file).
|
||
|
|
|
||
|
|
`example: check_encoder.c, check_securityLayer.c`
|
||
|
|
|
||
|
|
### Checking returns, error logging and cleanup
|
||
|
|
|
||
|
|
Error handling and corresponding reactions like cleanup, logging or updating
|
||
|
|
internal error states is important for a stable program flow.
|
||
|
|
|
||
|
|
For checking return values within functions there are specific macros that are used.
|
||
|
|
Handling check values and errors belongs to three different categories:
|
||
|
|
|
||
|
|
1. Handling `UA_StatusCode` check values
|
||
|
|
2. Handling memory allocation errors
|
||
|
|
3. Handling general `boolean` check values
|
||
|
|
|
||
|
|
A very frequent use case is the checking of a `UA_StatusCode` return value and
|
||
|
|
performing a specific action upon error. The workflow and usage is the same for
|
||
|
|
all of the above categories. The basic check macro follows the structure
|
||
|
|
`UA_CHECK_STATUS(STATUSCODE, EVAL_ON_ERROR)`, performing a check on the
|
||
|
|
`STATUSCODE` and execute `EVAL_ON_ERROR` if an erroneous state is detected. The
|
||
|
|
below example thus checks `rv` and executes `return rv` on error.
|
||
|
|
|
||
|
|
```c
|
||
|
|
UA_StatusCode rv = someAction();
|
||
|
|
UA_CHECK_STATUS(rv, return rv);
|
||
|
|
```
|
||
|
|
|
||
|
|
Similar to the above there exist logging variants for each check macro that logs
|
||
|
|
to a specific logging level. The macro names are appended with a suffix
|
||
|
|
`_FATAL`, `_ERROR`, `_WARN` or `_INFO`.
|
||
|
|
|
||
|
|
In this code snippet, the status code gets checked with a specified error
|
||
|
|
action, while logging a specified error level message:
|
||
|
|
|
||
|
|
```c
|
||
|
|
UA_Logger *logger = &server->config.logger;
|
||
|
|
|
||
|
|
UA_StatusCode rv = someAction();
|
||
|
|
/* logging "error" level with the "_ERROR" suffix */
|
||
|
|
UA_CHECK_STATUS_ERROR(rv, return rv, logger, UA_LOGCATEGORY_SERVER, "my message");
|
||
|
|
```
|
||
|
|
|
||
|
|
In some situations it is nice to use the `goto` command in C. This is the case
|
||
|
|
e.g. for jumping to cleanup routines that are used to perform a series of commands.
|
||
|
|
|
||
|
|
For the development of `open62541` we employ a specific rule for which cleanup
|
||
|
|
routines should be used: **If the same cleanup routine is used from at least two
|
||
|
|
places in the same function then this routine should be called via a `goto`
|
||
|
|
statement.**
|
||
|
|
|
||
|
|
In this example e.g. only the second error check needs to free up some memory upon encountering
|
||
|
|
an error, thus a jump to a cleanup routine is not indicated.
|
||
|
|
|
||
|
|
```c
|
||
|
|
void noCleanupRoutine(void *data) {
|
||
|
|
|
||
|
|
data = malloc(sizeof(int));
|
||
|
|
UA_CHECK_MEM(data, return UA_STATUSCODE_BADOUTOFMEMORY);
|
||
|
|
|
||
|
|
UA_StatusCode rv = do_something();
|
||
|
|
UA_CHECK_STATUS(rv, free(data); return rv);
|
||
|
|
|
||
|
|
return UA_STATUSCODE_GOOD;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
The below example on the other hand, shows two functions that use the same
|
||
|
|
routine for cleanup. A `goto` routine should thus be used.
|
||
|
|
|
||
|
|
```c
|
||
|
|
void yesCleanupRoutine(void *data) {
|
||
|
|
|
||
|
|
data = malloc(sizeof(int));
|
||
|
|
UA_CHECK_MEM(data, return UA_STATUSCODE_BADOUTOFMEMORY);
|
||
|
|
|
||
|
|
/* jumps to cleanup routine upon encountering a bad statuscode */
|
||
|
|
UA_StatusCode rv = do_something();
|
||
|
|
UA_CHECK_STATUS(rv, goto cleanup);
|
||
|
|
|
||
|
|
/* jumps to the same cleanup routine */
|
||
|
|
rv = do_something_else();
|
||
|
|
UA_CHECK_STATUS(rv, goto cleanup);
|
||
|
|
|
||
|
|
return UA_STATUSCODE_GOOD;
|
||
|
|
|
||
|
|
cleanup:
|
||
|
|
free(data);
|
||
|
|
return rv;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
In this example only the status codes are checked and the corresponding
|
||
|
|
action is evaluated on error. There is no message logging included.
|
||
|
|
|
||
|
|
```c
|
||
|
|
static UA_StatusCode
|
||
|
|
foo(int *errorCounter) {
|
||
|
|
|
||
|
|
UA_StatusCode rv = do_something();
|
||
|
|
/* if rv != UA_STATUSCODE_GOOD then "return rv" gets evaluated */
|
||
|
|
UA_CHECK_STATUS(rv, return rv);
|
||
|
|
|
||
|
|
rv = do_another_thing();
|
||
|
|
|
||
|
|
/*
|
||
|
|
EVAL_ON_ERROR can take multiple statements
|
||
|
|
(e.g. first modifying some value then return the error code)
|
||
|
|
*/
|
||
|
|
UA_CHECK_STATUS(rv, rv = UA_STATUSCODE_BAD; errorCounter++; return rv);
|
||
|
|
|
||
|
|
return UA_STATUSCODE_GOOD;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
In this example status codes are checked while logging message with different
|
||
|
|
levels and a specified `LOGCATEGORY` are generated with a given logger.
|
||
|
|
|
||
|
|
```c
|
||
|
|
static UA_StatusCode
|
||
|
|
foo(UA_Server *server) {
|
||
|
|
|
||
|
|
/* assign the logger for later simple use */
|
||
|
|
UA_Logger *logger = &server->config.logger;
|
||
|
|
|
||
|
|
UA_StatusCode rv = do_something_error();
|
||
|
|
|
||
|
|
/*
|
||
|
|
if rv != UA_STATUSCODE_GOOD then
|
||
|
|
an error logging message is generated and
|
||
|
|
"return rv" gets evaluated
|
||
|
|
*/
|
||
|
|
UA_CHECK_STATUS_ERROR(rv, return rv,
|
||
|
|
logger, UA_LOGCATEGORY_SERVER,
|
||
|
|
"My error logging message with special info: %d", 42);
|
||
|
|
|
||
|
|
rv = do_something_warn();
|
||
|
|
UA_CHECK_STATUS_WARN(rv, return rv,
|
||
|
|
logger, UA_LOGCATEGORY_SERVER,
|
||
|
|
"My warning logging message with special info: %d", 42);
|
||
|
|
|
||
|
|
rv = do_something_info();
|
||
|
|
UA_CHECK_STATUS_INFO(rv, return rv,
|
||
|
|
logger, UA_LOGCATEGORY_SERVER,
|
||
|
|
"My info logging message with special info: %d", 42);
|
||
|
|
|
||
|
|
rv = do_something_fatal();
|
||
|
|
UA_CHECK_STATUS_FATAL(rv, return rv,
|
||
|
|
logger, UA_LOGCATEGORY_SERVER,
|
||
|
|
"My fatal logging message with special info: %d", 42);
|
||
|
|
|
||
|
|
|
||
|
|
return UA_STATUSCODE_GOOD;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
All the other check macros function analogously to the above `UA_CHECK_STATUS` examples,
|
||
|
|
with a short example to explain the basic usage:
|
||
|
|
|
||
|
|
```c
|
||
|
|
static UA_StatusCode
|
||
|
|
foo() {
|
||
|
|
|
||
|
|
/* assign the logger for later simple use */
|
||
|
|
UA_Logger *logger = &server->config.logger;
|
||
|
|
|
||
|
|
UA_Boolean mustBeTrue = do_something();
|
||
|
|
/* if mustBeTrue != true then "return UA_STATUSCODE_BAD" gets evaluated */
|
||
|
|
UA_CHECK(mustBeTrue, return UA_STATUSCODE_BAD);
|
||
|
|
|
||
|
|
UA_StatusCode rv = UA_STATUSCODE_GOOD;
|
||
|
|
/*
|
||
|
|
EVAL_ON_ERROR can take multiple statements
|
||
|
|
(e.g. first assigning some value then return the error code)
|
||
|
|
*/
|
||
|
|
UA_CHECK(mustBeTrue, rv = UA_STATUSCODE_BAD; return rv);
|
||
|
|
|
||
|
|
UA_Boolean mustBeTrue = do_something_else();
|
||
|
|
/*
|
||
|
|
if mustBeTrue != true then
|
||
|
|
an error logging message is generated and
|
||
|
|
"return UA_STATUSCODE_BAD" gets evaluated
|
||
|
|
*/
|
||
|
|
UA_CHECK_ERROR(mustBeTrue, return UA_STATUSCODE_BAD,
|
||
|
|
logger, UA_LOGCATEGORY_SERVER,
|
||
|
|
"My logging message with special info: %d", 42);
|
||
|
|
|
||
|
|
|
||
|
|
void *data = malloc(1, 1);
|
||
|
|
UA_CHECK_MEM(data, return UA_STATUSCODE_BADOUTOFMEMORY);
|
||
|
|
|
||
|
|
void *data2 = malloc(1, 1);
|
||
|
|
UA_CHECK_MEM_ERROR(data2, return UA_STATUSCODE_BADOUTOFMEMORY,
|
||
|
|
logger, UA_LOGCATEGORY_SERVER,
|
||
|
|
"My logging message with special info: %d", 42);
|
||
|
|
|
||
|
|
return UA_STATUSCODE_GOOD;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Still unsure?
|
||
|
|
|
||
|
|
If any questions arise concerning code style, feel free to start an issue.
|