# Tutorial: Define custom timeline feature {#tutorial_define_timeline_feature} In this tutorial we will learn how to extend timeline capabilities by defining custom timeline features. ## Before you start... We recommend you to go through the @ref tutorial_hello_world tutorial to make sure you have the %HawkTracer library properly installed. ## Problem statement We'd like to know how many events we pushed to a specific timeline object. ## Code Please copy the text below to a files named `hawktracer-custom-timeline-feature.c`, `push-counter-feature.c` and `push-counter-feature.h` (you can find those files [in the repository as well](@repocodeurl/examples/tutorials/custom_timeline_feature/)). **hawktracer-custom-timeline-feature.c** @include examples/tutorials/custom_timeline_feature/hawktracer-custom-timeline-feature.c **push-counter-feature.h** @include examples/tutorials/custom_timeline_feature/push-counter-feature.h **push-counter-feature.c** @include examples/tutorials/custom_timeline_feature/push-counter-feature.c [comment]: # (TODO: create section "building examples") ## Walkthrough We assume you already went through the @ref tutorial_hello_world tutorial, so we only focus here on the new part. ### Defining the feature We start with defining the feature. ```cpp typedef struct { HT_Feature base; int counter; } PushCounterFeature; HT_FEATURE_DEFINE(PushCounterFeature, push_counter_feature_destroy) ``` First, we define the structure that holds all the required information about the feature. We agreed that we want to count number of events being pushed to a timeline, therefore we need a `counter` field. All the features must inherit from `HT_Feature` class, so we need to create `base` field of this type as a first one in the structure. HT_FEATURE_DEFINE() is a helper macro that defines a few helper methods which we use in the implementation. It takes struct name and destroy callback. ```cpp HT_ErrorCode push_counter_feature_enable(HT_Timeline* timeline) ``` We'd like the feature to be easily accessible to our users (including ourselves), so we define `push_counter_feature_enable()` that will be used to enable the feature for a specific timeline. As enabling feature might fail because of many reasons, we return HT_ErrorCode to inform our users about exact problem. ```cpp PushCounterFeature* feature = PushCounterFeature_alloc(); ``` We allocate an instance of the object using `PushCounterFeature_alloc()` method - the method was auto-generated by HT_FEATURE_DEFINE(). It allocates memory (using ht_alloc()) but also initializes the `base` field of the structure. If you want to use allocation method different than ht_alloc(), you need to make sure that you initialize the structure on your own. ```cpp if (feature == NULL) { return HT_ERR_OUT_OF_MEMORY; } ``` Allocating the feature might fail, so we need to handle this scenario correctly. ```cpp feature->counter = 0; ``` We need to initialize the data structure. In our case, it means reseting the counter value. ```cpp return ht_timeline_set_feature(timeline, (HT_Feature*)feature); ``` When the feature is initialized, we can attach it to the timeline, using ht_timeline_set_feature. From now on, the timeline manages the memory of the feature (even if the function doesn't complete successfully, it will still release the feature). ```cpp static void push_counter_feature_destroy(HT_Feature* feature) { PushCounterFeature* push_counter_feature = (PushCounterFeature*)feature; ht_free(push_counter_feature); } ``` We also need to define a method for destroying the feature. As mentioned above, by default HawkTracer uses ht_alloc() method for allocating the memory for the object, therefore we use ht_free() for releasing it. If the feature object allocated additional resources, the method is a right place to release them. ### Defining the feature logic We defined the feature, but without the logic implemented for the feature, it's kind of useless. ```cpp void push_counter_feature_push_event(HT_Timeline* timeline, HT_Event* event) ``` We'll define a method which will push an event to a timeline, and at the same time increase the counter of the feature. ```cpp PushCounterFeature* feature = PushCounterFeature_from_timeline(timeline); ``` Please note we don't pass the feature as an argument to the function. In order to get it, HT_DEFINE_FEATURE() generates `PushCounterFeature_from_timeline()` function that gets the feature from the timeline. ```cpp if (!feature) { printf("Feature has not been enabled for the pipeline\n"); return; } ``` The timeline being passed to the function might not have this feature enabled, so we need to check that before executing the logic. ```cpp ht_timeline_push_event(timeline, event); feature->counter++; ``` This is the actual logic of the feature - we push the event to the timeline, and increment the counter. ```cpp int push_counter_feature_get_count(HT_Timeline* timeline) { PushCounterFeature* feature = PushCounterFeature_from_timeline(timeline); if (!feature) { return -1; } return feature->counter; } ``` We also define a simple method for returning the counter to the user. ### Declaring interface of the feature In the previous paragraph we defined the feature. Now, we need to expose an interface to the user so she can use it in her code. ```cpp HT_ErrorCode PushCounterFeature_register(void); HT_ErrorCode push_counter_feature_enable(HT_Timeline* timeline); int push_counter_feature_get_count(HT_Timeline* timeline); void push_counter_feature_push_event(HT_Timeline* timeline, HT_Event* event); ``` Apart from the three methods we defined in the previous paragraph, there's one extra method we expose to users: `HT_ErrorCode PushCounterFeature_register()` - we didn't implement the function, but it was auto-generated by HT_DEFINE_FEATURE(). The feature must be registered before usage, so we need to expose the function so she can register it at any time in her code. ### Using the feature in the code ```cpp PushCounterFeature_register(); ``` Before we start, the feature must be registered. The function should be called after ht_init(), but before any usage of the feature. ```cpp push_counter_feature_enable(timeline); ``` We enable the feature for the timeline. We defined a nice function that wraps all the initializations, so our user needs to call only one function to attach the feature to the timeline. ```cpp push_counter_feature_push_event(timeline, &event); ``` Instead of using ht_timeline_push_event() function, we use function defined for our feature, so we can keep track of number of events being pushed to the timeline. ```cpp printf("Number of events pushed to the timeline: %d\n", push_counter_feature_get_count(timeline)); ``` At the end of the program we print the count, so we now how many events we pushed to the timeline. ```cpp ht_timeline_destroy(timeline); ``` There's no need for destroying the feature - timeline destructor takes care of releasing the object by calling `push_counter_feature_destroy()` function. ### Program output After running the program, you should see the following output on the screen: ```bash $ ./custom-timeline-feature Number of events pushed to the timeline: 10 ```