Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iox-1391 Move vocabulary types to separate module #1791

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .clang-tidy-diff-scans.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
./iceoryx_hoofs/container/include/iox/**/*
./iceoryx_hoofs/test/moduletests/test_container_*

./iceoryx_hoofs/vocabulary/**/*
./iceoryx_hoofs/test/moduletests/test_vocabulary_*

# IMPORTANT:
# after the first # everything is considered a comment, add new files and
# directories only at the top of this file
Expand Down
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ codebase follows these rules, things are work in progress.
[section](CONTRIBUTING.md#external-dependencies) below
7) **Always use `iox::log::Logger`**, instead of `printf()`
8) **Always use `iox::ErrorHandler` or `iox::cxx::Expects`/`iox::cxx::Ensures`**, when an error occurs that cannot or
shall not be propagated via an `iox::cxx::expected`
shall not be propagated via an `iox::expected`
9) **Not more than two-level nested namespaces**, three-level nested namespace can be used sparsely

See [error-handling.md](./doc/design/error-handling.md) for additional
Expand All @@ -130,9 +130,9 @@ For formatting and linting rules on Bazel files see the [installation guide for
* Class members start with `m_`: `m_myMember`
* Public members of structs and classes do not have the `m_` prefix
* Namespaces in `lower_snake_case` : `my_namespace`
* Aliases have a `_t` postfix : `using FooString_t = iox::cxx::string<100>;`
* Objects created from a method returning a `cxx::optional<Foo>` shall be named `maybeFoo`
* Objects created from a method returning a `cxx::expected<Foo, FooError>` shall
* Aliases have a `_t` postfix : `using FooString_t = iox::string<100>;`
* Objects created from a method returning a `iox::optional<Foo>` shall be named `maybeFoo`
* Objects created from a method returning a `iox::expected<Foo, FooError>` shall
contain the name `result` e.g. `getChunkResult` for the method `getChunk()`

### clang-tidy suppressions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ return CaproMessageType::CONNECT
PortManager -> PortManager ++ : sendToAllMatchingServerPorts

PortManager -> ClientPortRouDi ++ : dispatchCaProMessageAndGetPossibleResponse(CaproMessageType::NACK)
return cxx::nullopt
return iox::nullopt

return

Expand Down Expand Up @@ -36,7 +36,7 @@ PortManager -> ServerPortRouDi ++ : dispatchCaProMessageAndGetPossibleResponse(C
return CaproMessageType::ACK

PortManager -> ClientPortRouDi ++ : dispatchCaProMessageAndGetPossibleResponse(CaproMessageType::ACK)
return cxx::nullopt
return iox::nullopt

return

Expand All @@ -56,7 +56,7 @@ PortManager -> ServerPortRouDi ++ : dispatchCaProMessageAndGetPossibleResponse(C
return CaproMessageType::ACK

PortManager -> ClientPortRouDi ++ : dispatchCaProMessageAndGetPossibleResponse(CaproMessageType::ACK)
return cxx::nullopt
return iox::nullopt

return

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ package "iceoryx instance e.g. electronic control unit" {
component "User App" {
component DiscoveryInfoA <<object>> {
artifact DiscoveryInfoNoteA [
+findService() : cxx::expected<InstanceContainer, FindServiceError>
+findService() : iox::expected<InstanceContainer, FindServiceError>
-m_subscriber : Subscriber<ServiceRegistryTopic>
-m_lastServiceRegistry : ServiceRegistryTopic
]
Expand All @@ -24,7 +24,7 @@ package "iceoryx instance e.g. electronic control unit" {
component "DDS Gateway" {
component DiscoveryInfoB <<object>> {
artifact DiscoveryInfoNoteB [
+findService() : cxx::expected<InstanceContainer, FindServiceError>
+findService() : iox::expected<InstanceContainer, FindServiceError>
-m_subscriber : Subscriber<ServiceRegistryTopic>
-m_lastServiceRegistry : ServiceRegistryTopic
]
Expand Down
2 changes: 1 addition & 1 deletion doc/design/draft/service-discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ e.g. at startup the IPC channel can become a bottleneck.

#### Runtime

* `cxx::expected<InstanceContainer, FindServiceError> PoshRuntime::findService(capro::ServiceDescription)`
* `iox::expected<InstanceContainer, FindServiceError> PoshRuntime::findService(capro::ServiceDescription)`
* Sends message over IPC channel

#### RouDi
Expand Down
24 changes: 12 additions & 12 deletions doc/design/error-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ To this end it is necessary to eliminate the use of all potentially throwing (th

#### Alternatives to exceptions

As an alternative to exceptions we use the error handler and a variation of return codes in the form of ``cxx::expected``, described below. ``cxx::expected`` can be used to communicate the error to the caller, who has to decide whether to handle the error itself or propagate it further (e.g. as another ``cxx::expected``). Error handling itself is performed by the error handler which handles errors occurring in the subcomponents of ``iceoryx::posh``.
As an alternative to exceptions we use the error handler and a variation of return codes in the form of ``iox::expected``, described below. ``iox::expected`` can be used to communicate the error to the caller, who has to decide whether to handle the error itself or propagate it further (e.g. as another ``iox::expected``). Error handling itself is performed by the error handler which handles errors occurring in the subcomponents of ``iceoryx::posh``.

### Error handler

Expand Down Expand Up @@ -124,22 +124,22 @@ Although Expects end Ensures behave the same, the former is used to signify a pr

Examples include expecting pointers that are not null (as input, intermediate or final result) or range checks of variables.

### `cxx::expected`
### `iox::expected`

``cxx::expected<T, E>`` is a template which either holds the result of the computation of type ``T`` or an object of error type ``E``. The latter can be used to obtain additional information about the error, e.g. an error code.
``iox::expected<T, E>`` is a template which either holds the result of the computation of type ``T`` or an object of error type ``E``. The latter can be used to obtain additional information about the error, e.g. an error code.
In a way this extends error codes and may act as kind of a replacement of exceptions. It is usually used as return type of functions which may fail for various reasons and should be used if the error is supposed to be delegated to the caller and handled in the caller context.
It is also possible to further propagate the error to the next function in the call-stack (since this must be done explicitly, which is comparable to rethrowing an exception).

It is possible to use the ``nodiscard`` option to force the user to handle the returned ``cxx::expected``.
It is possible to use the ``nodiscard`` option to force the user to handle the returned ``iox::expected``.
If the error cannot be handled at a higher level by the caller, the error handler needs to be used.

Examples include wrapping third party API functions that return error codes or obtaining a value from a container when this can fail for some reason (e.g. container is empty). If no additional information about the error is available or required, ``cxx::optional<T>`` can be used instead.
Examples include wrapping third party API functions that return error codes or obtaining a value from a container when this can fail for some reason (e.g. container is empty). If no additional information about the error is available or required, ``iox::optional<T>`` can be used instead.

### Error handling in posh

Error logging shall be done by the logger only, no calls to ``std::cerr`` or similar should be performed.

All the methods presented (``cxx::expected``, ``Expects`` and ``Ensures`` and the error handler) can be used in posh. The appropriate way depends on the type of error scenario (cf. the respective sections for examples). The error handler should be considered the last option.
All the methods presented (``iox::expected``, ``Expects`` and ``Ensures`` and the error handler) can be used in posh. The appropriate way depends on the type of error scenario (cf. the respective sections for examples). The error handler should be considered the last option.

### Error handling in hoofs

Expand All @@ -148,7 +148,7 @@ Some files may still use `std::cout`/`std::cerr`/`std::clog` but this should be

The error handler cannot be used in hoofs.

Whether it is appropriate to use ``cxx::expected`` even if STL compatibility is broken by doing so depends on the circumstances and needs to be decided on a case-by-case basis. If the function has no STL counterpart ``cxx::expected`` can be used freely to communicate potential failure to the caller.
Whether it is appropriate to use ``iox::expected`` even if STL compatibility is broken by doing so depends on the circumstances and needs to be decided on a case-by-case basis. If the function has no STL counterpart ``iox::expected`` can be used freely to communicate potential failure to the caller.

It should be noted that since currently `Expects` and `Ensures` are active at release mode, prolific usage of these will incur a runtime cost. Since this is likely to change in the future, it is still advised to use them to document the developer's intentions.

Expand Down Expand Up @@ -220,13 +220,13 @@ int32_t myAlgorithm(int32_t* ptr) {
Note that in the case of ``nullptr`` checks it is also an option to use references in arguments (or ``not_null`` if it is supposed to be stored since references are not copyable). It should be considered that ``not_null`` incurs a runtime cost, which may be undesirable.
When Expects and Ensures are implemented to leave no trace in release mode, we do not incur a runtime cost using them. For this reason, it is advised to use them to document and verify assumptions where appropriate.

### `cxx::expected`
### `expected`

This example checks the arguments and if they are valid proceeds to compute a result and returns it.
Otherwise it creates an Error object from an errorCode and returns it.

```cpp
cxx::expected<SomeType, Error> func(Arg arg) {
expected<SomeType, Error> func(Arg arg) {
int32_t errorCode = checkArg(arg);
if(isNoError(errorCode)) {
SomeType result = computeResult(arg);
Expand Down Expand Up @@ -336,8 +336,8 @@ In this section we briefly describe ways to potentially improve or extend functi

Allow deactivation in release mode, but it should still be possible to leave them active in release mode as well if desired. Deactivation in debug mode can also be considered but is less critical. Deactivation should eliminate all runtime overhead (i.e. condition evaluation).

### cxx::expected
### expected

1. Consider renaming ``cxx::expected`` to ``cxx::result``, which is more in line with languages such as Rust and conveys the meaning more clearly.
1. Add ``has_value`` (in addition to ``has_error``) for consistency with ``cxx::optional``.
1. Consider renaming ``expected`` to ``cxx::result``, which is more in line with languages such as Rust and conveys the meaning more clearly.
1. Add ``has_value`` (in addition to ``has_error``) for consistency with ``optional``.
1. Improve the monadic error handling (beyond ``and_then``, ``or_else``) to allow for better pipelining of multiple consecutive calls (especially in the success case). This requires careful consideration of supported use cases and intended behavior but can reduce control flow code (``if ... else ...``) in error cases considerably.
2 changes: 1 addition & 1 deletion doc/design/request_response_communication.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ As optimization, the `lastKnownQueueIndex` will be used to do a fast lookup and
```cpp
class ChunkDistributor {
...
cxx::expected<Error> deliverToQueue(cxx::UniqueId uniqueQueueId, uint32_t lastKnownQueueIndex, mepoo::SharedChunk chunk);
iox::expected<Error> deliverToQueue(cxx::UniqueId uniqueQueueId, uint32_t lastKnownQueueIndex, mepoo::SharedChunk chunk);
...
};
```
Expand Down
4 changes: 2 additions & 2 deletions doc/website/concepts/best-practice-for-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ In general, the tests should be written in a fashion to not crash the applicatio
It must be assumed that the implementation is broken and only a successful test run can prove otherwise.
The `sut` (system under test) might return a `nullptr` instead of the expected valid pointer, so `nullptr` check has to be done with an `ASSERT_*` to gracefully abort the current test.
Just using an `EXPECT_*` for the check is not sufficient since the potential `nullptr` will be dereferenced later on and will crash the application.
The same applies to other potential dangerous operations, like accessing the value of a `cxx::optional` or `cxx::expected` or an out of bounds access of a `cxx::vector`.
The same applies to other potential dangerous operations, like accessing the value of a `iox::optional` or `iox::expected` or an out of bounds access of a `iox::vector`.

Last but not least, apply the **DRY** principle (don't repeat yourself) and use typed and parameterized tests to check multiple implementations and variations without repeating much code.

Expand Down Expand Up @@ -289,7 +289,7 @@ TEST_F(MyTest, TestName)
- name the test object `sut` to make clear which object is tested
- don't use magic numbers
- instantiate objects on the stack or use smart pointers for large objects and avoid manual memory management with new/delete
- use `ASSERT_*` before doing a potential dangerous action which might crash the test application, like accessing a `nullptr` or a `cxx::optional` with a `nullopt`
- use `ASSERT_*` before doing a potential dangerous action which might crash the test application, like accessing a `nullptr` or a `iox::optional` with a `iox::nullopt`
- use mocks to reduce the complexity of the test arrangement
- apply the **DRY** principle by using typed and parameterized tests
- wrap the tests in an anonymous namespace
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
Many parts of the iceoryx C++ API follow a functional programming approach and allow the user to specify functions
which handle the possible cases, e.g. what should happen when data is received.

This is very flexible but requires using the monadic types `iox::cxx::expected` and `iox::cxx::optional`, which we
This is very flexible but requires using the monadic types `iox::expected` and `iox::optional`, which we
introduce in the following sections.

## Optional

The type `iox::cxx::optional<T>` is used to indicate that there may or may not be a value of a specific type `T`
The type `iox::optional<T>` is used to indicate that there may or may not be a value of a specific type `T`
available. This is essentially the 'maybe [monad](https://en.wikipedia.org/wiki/Monad_%28functional_programming%29)' in
functional programming.

The `iox::cxx::optional` behaves like the [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional),
The `iox::optional` behaves like the [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional),
except that it terminates the application where `std::optional` would throw an exception or exhibit undefined behavior.

Assuming we have some optional (usually the result of some computation)
Expand Down Expand Up @@ -61,19 +61,20 @@ optional<int> result = 73;
result = 37;
```

If the optional is default initialized, it is automatically set to its null value of type `iox::cxx::nullopt_t`.
This can be also done directly by using the constant `iox::cxx::nullopt`
If the optional is default initialized, it is automatically set to its null value of type `iox::nullopt_t`.
This can be also done directly by using the constant `iox::nullopt`

```cpp
result = iox::cxx::nullopt;
result = iox::nullopt;
```

For a complete list of available functions see
[`optional.hpp`](../../../iceoryx_hoofs/include/iceoryx_hoofs/cxx/optional.hpp).
[`optional.hpp`](../../../iceoryx_hoofs/vocabulary/include/iox/optional.hpp).


## Expected

`iox::cxx::expected<T, E>` generalizes `iox::cxx::optional` by admitting a value of another type `E` instead of
`iox::expected<T, E>` generalizes `iox::optional` by admitting a value of another type `E` instead of
no value at all, i.e. it contains either a value of type `T` or `E`. In this way, `expected` is a special case of
the 'either monad'. It is usually used to pass a value of type `T` or an error that may have occurred, i.e. `E` is the
error type.
Expand All @@ -84,7 +85,7 @@ For more information on how it is used for error handling see
Assume we have `E` as an error type, then we can create a value

```cpp
iox::cxx::expected<int, E> result(iox::cxx::success<int>(73));
iox::expected<int, E> result(iox::success<int>(73));
```

and use the value or handle a potential error
Expand All @@ -105,7 +106,7 @@ else
If we need an error value, we set

```cpp
result = iox::cxx::error<E>(errorCode);
result = iox::error<E>(errorCode);
```

which assumes that `E` can be constructed from an `errorCode`.
Expand All @@ -120,9 +121,9 @@ result.and_then(handleValue).or_else(handleError);

There are more convenience functions such as `value_or` which provides the value or an alternative specified by the
user. These can be found in
[`expected.hpp`](../../../iceoryx_hoofs/include/iceoryx_hoofs/cxx/expected.hpp).
[`expected.hpp`](../../../iceoryx_hoofs/vocabulary/include/iox/expected.hpp).

Note that when we move an `expected`, the origin contains a moved `T` or `E`, depending on the content before the move.
This reflects the behavior of moving the content out of the `iox::cxx::expected` as in
`auto foo = std::move(bar.value());` with `bar` being an `iox::cxx::expected`.
This reflects the behavior of moving the content out of the `iox::expected` as in
`auto foo = std::move(bar.value());` with `bar` being an `iox::expected`.
Like all objects, `T` and `E` must therefore be in a well defined state after the move.
6 changes: 3 additions & 3 deletions doc/website/getting-started/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ else
}
```

Here `result` is an `expected` and hence we may get an error. This can happen if we try to loan too many samples
Here `result` is an `iox::expected` and hence we may get an error. This can happen if we try to loan too many samples
and exhaust memory. We have to handle this potential error since the expected class has the `nodiscard` keyword
attached. This means we get a warning (or an error when build in strict mode) when we don't handle it. We could
also explicitly discard it with `IOX_DISCARD_RESULT` which is discouraged. If you want to know more about
`expected`, take a look at
`iox::expected`, take a look at
[How optional and error values are returned in iceoryx](../concepts/how-optional-and-error-values-are-returned-in-iceoryx.md).
Let's create a corresponding subscriber.

Expand Down Expand Up @@ -335,7 +335,7 @@ The API is offered in two languages, C++ and C. Detailed information can be foun
[C example](../../../iceoryx_examples/icedelivery_in_c).

Many parts of the C++ API follow a functional programming approach which is less error-prone. This requires using
the monadic types `cxx::expected` and `cxx::optional` which are introduced
the monadic types `iox::expected` and `iox::optional` which are introduced
[here](../concepts/how-optional-and-error-values-are-returned-in-iceoryx.md).

With the C++ API, we distinguish between the `typed API` and the `untyped API`. In the typed API, the underlying
Expand Down
Loading