# user-header ## Introduction This example demonstrates how to leverage the user-header to send custom meta-information with each sample. Specifically, we want to send a timestamp alongside the sample. The example contains code for the typed and untyped C++ API as well as for the C language binding. ## Expected Output [![asciicast](https://asciinema.org/a/410691.svg)](https://asciinema.org/a/410691) ## Code walkthrough The examples uses the user-header and user-payload which is defined in `user_header_and_payload_types.hpp` for the C++ API and in `user_header_and_payload_types.h` for the C API. The user-header consists of a simple `uint64_t` which is used to transmit a timestamp and the user-payload is a Fibonacci number. This are the definitions for the C++ API: ```cpp struct Header { uint64_t publisherTimestamp{0}; }; ``` ```cpp struct Data { uint64_t fibonacci{0}; }; ``` This are the definitions for the C API: ```c typedef struct { uint64_t publisherTimestamp; } Header; ``` ```c typedef struct { uint64_t fibonacci; } Data; ``` ### Publisher Typed C++ API At first, there are includes for the user-header and user-payload types and the iceoryx includes for publisher and runtime. ```cpp #include "user_header_and_payload_types.hpp" #include "iceoryx_hoofs/posix_wrapper/signal_watcher.hpp" #include "iceoryx_posh/popo/publisher.hpp" #include "iceoryx_posh/runtime/posh_runtime.hpp" ``` Next, the iceoryx runtime is initialized. With this call, the application will be registered at `RouDi`, the routing and discovery daemon. ```cpp constexpr char APP_NAME[] = "iox-cpp-user-header-publisher"; iox::runtime::PoshRuntime::initRuntime(APP_NAME); ``` Now, we create the publisher. Unlike the other examples, this uses the second template parameter to define the user-header. This is the only change compared to the other examples with the typed C++ API. ```cpp iox::popo::Publisher publisher({"Example", "User-Header", "Timestamp"}); ``` In the main loop, a Fibonacci sequence is created and every second a number is published. The Fibonacci number is passed to the `loan` method which construct the `Data` object if the loaning was successful. This example uses the functional approach with `and_then` and `or_else`. Please have a look at the `icehello` example if you prefer a more traditional approach. ```cpp uint64_t timestamp = 42; uint64_t fibonacciLast = 0; uint64_t fibonacciCurrent = 1; while (!iox::posix::hasTerminationRequested()) { auto fibonacciNext = fibonacciCurrent + fibonacciLast; fibonacciLast = fibonacciCurrent; fibonacciCurrent = fibonacciNext; publisher.loan(Data{fibonacciCurrent}) .and_then([&](auto& sample) { // ... }) .or_else([&](auto& error) { // ... }); constexpr uint64_t MILLISECONDS_SLEEP{1000U}; std::this_thread::sleep_for(std::chrono::milliseconds(MILLISECONDS_SLEEP)); timestamp += MILLISECONDS_SLEEP; } ``` When the loaning was successful, we get a sample and can access it in the `and_then` branch. The sample has a `getUserHeader` method which returns a reference to the user-header we specified with the template parameter. In this case it's the `Header` struct and we set the `publisherTimestamp`. ```cpp sample.getUserHeader().publisherTimestamp = timestamp; sample.publish(); std::cout << APP_NAME << " sent data: " << fibonacciCurrent << " with timestamp " << timestamp << "ms" << std::endl; ``` If the loaning fails, the `or_else` branch is executed, which prints an error message ```cpp std::cout << APP_NAME << " could not loan sample! Error code: " << error << std::endl; ``` ### Publisher Untyped C++ API The example with the untyped C++ publisher is quite similar to the one with the typed publisher. The few differences will be discussed in this section. At first there is a different include ```cpp #include "iceoryx_posh/popo/untyped_publisher.hpp" ``` When the publisher is created, there is also no notion of a user-header and it looks exactly the same like already shown in other examples. ```cpp iox::popo::UntypedPublisher publisher({"Example", "User-Header", "Timestamp"}); ``` Variations come again into play when the chunk is loaned. Since the API is untyped, the parameter for the user-header have to be provided with the `loan` call. These are optional parameter and are set to values indicating no user-header by default. ```cpp publisher.loan(sizeof(Data), alignof(Data), sizeof(Header), alignof(Header)) .and_then([&](auto& userPayload) { // ... }) .or_else([&](auto& error) { // ... }); ``` Lastly, since the untyped C++ API returns a `void` pointer to the user-payload, there is an intermediate step to access the user-header by obtaining a `ChunkHeader` first, which provides a method to get the user-header. This also return a `void` pointer, which makes a cast to the actual type necessary. ```cpp auto header = static_cast(iox::mepoo::ChunkHeader::fromUserPayload(userPayload)->userHeader()); header->publisherTimestamp = timestamp; auto data = static_cast(userPayload); data->fibonacci = fibonacciCurrent; publisher.publish(userPayload); std::cout << APP_NAME << " sent data: " << fibonacciCurrent << " with timestamp " << timestamp << "ms" << std::endl; ``` ### Publisher C API The example for the C API is similar to the one in `icedelivery_in_c` and therefore only the user-header related parts are looked into. The overall structure is the same like in the typed and untyped C++ API counterparts. The functions to access the user-header are located in the following include ```cpp #include "iceoryx_binding_c/chunk.h" ``` Similar to the untyped C++ API, the user-header parameter are specified with the loan function. Since C does not have overloading, this is done by a different function ```cpp void* userPayload; const uint32_t ALIGNMENT = 8; enum iox_AllocationResult res = iox_pub_loan_aligned_chunk_with_user_header( publisher, &userPayload, sizeof(Data), ALIGNMENT, sizeof(Header), ALIGNMENT); ``` Like with the untyped C++ API, the path to the user-header needs an intermediate step with the `iox_chunk_header_t` and explicit casting to since the functions return `void` pointer. ```cpp iox_chunk_header_t* chunkHeader = iox_chunk_header_from_user_payload(userPayload); Header* header = (Header*)iox_chunk_header_to_user_header(chunkHeader); header->publisherTimestamp = timestamp; Data* data = (Data*)userPayload; data->fibonacci = fibonacciCurrent; iox_pub_publish_chunk(publisher, userPayload); // explicit cast to unsigned long since on macOS an uint64_t is a different built-in type than on Linux printf("%s sent data: %lu with timestamp %ldms\n", APP_NAME, (unsigned long)fibonacciCurrent, (unsigned long)timestamp); fflush(stdout); ``` ### Subscriber Typed C++ API The boilerplate code for the subscriber is the same like for the publisher, therefore only the specific subscriber code is discussed in this section. To use the subscriber, `subscriber.hpp` needs to be included. Similar to the publisher, the subscriber requires the same additional template parameter when it is used with a user header. ```cpp iox::popo::Subscriber subscriber({"Example", "User-Header", "Timestamp"}); ``` The main loop is quite simple. The publisher is periodically polled and the data of the received sample is printed. Again, the user-header is accessed by the `getUserHeader` method of the sample. ```cpp while (!iox::posix::hasTerminationRequested()) { subscriber.take().and_then([&](auto& sample) { std::cout << APP_NAME << " got value: " << sample->fibonacci << " with timestamp " << sample.getUserHeader().publisherTimestamp << "ms" << std::endl; }); constexpr std::chrono::milliseconds SLEEP_TIME{100U}; std::this_thread::sleep_for(SLEEP_TIME); } ``` ### Subscriber Untyped C++ API On the subscriber side, the typed and untyped examples are even closer in similarity than the publisher example. The notable difference is the `take` method, which returns a `void` pointer to the user-payload. Like with the untyped publisher, in order to get the user-header the `ChunkHeader` must be obtained. Contrary to the untyped publisher, we must cast to a `const T*` type, like `const Header*` since we are not allowed to mutate the header from a subscriber. At the end, the chunk must be released to prevent chunk leaks. This is done by calling the `release` method with user-payload pointer. ```cpp subscriber.take().and_then([&](auto& userPayload) { auto header = static_cast(iox::mepoo::ChunkHeader::fromUserPayload(userPayload)->userHeader()); auto data = static_cast(userPayload); std::cout << APP_NAME << " got value: " << data->fibonacci << " with timestamp " << header->publisherTimestamp << "ms" << std::endl; subscriber.release(userPayload); }); ``` ### Subscriber C API Finally there is the C API example. Like with the publisher example for the C API we just take a look at the user-header specific parts. The overall structure is the same like in the typed and untyped C++ API counterparts. As we already have seen with the untyped C++ example, the call to take the sample is independent of the usage of a user-header. Likewise, the access to the user-header is done by the intermediate step of getting an `iox_chunk_header_t` first. Since we are not allowed to mutate the user-header from a subscriber, the respective functions with a `_const` suffix must be used. At last, the chunk is released in order to prevent chunk leaks. ```cpp const void* userPayload; if (iox_sub_take_chunk(subscriber, &userPayload) == ChunkReceiveResult_SUCCESS) { const iox_chunk_header_t* chunkHeader = iox_chunk_header_from_user_payload_const(userPayload); const Header* header = (const Header*)(iox_chunk_header_to_user_header_const(chunkHeader)); const Data* data = (const Data*)userPayload; // explicit cast to unsigned long since on macOS an uint64_t is a different built-in type than on Linux printf("%s got value: %lu with timestamp %ldms\n", APP_NAME, (unsigned long)data->fibonacci, (unsigned long)header->publisherTimestamp); fflush(stdout); iox_sub_release_chunk(subscriber, userPayload); } ```
[Check out User-Header on GitHub :fontawesome-brands-github:](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/user_header){ .md-button }