RTI Connext Modern C++ API
Version 5.3.0
|
Creating a custom content filter. More...
Creating a custom content filter.
By default, RTI Connext creates content filters with the DDS_SQL_FILTER, which implements a superset of the DDS-specified SQL WHERE clause. However, in many cases this filter may not be what you want. Some examples are:
This HOWTO explains how to write your own custom filter and is divided into the following sections:
A custom content filter is created by calling the DomainParticipant::register_contentfilter function with a rti::topic::CustomFilter. A CustomFilter is created with either a rti::topic::ContentFilter or rti::topic::WriterContentFilter.
A ContentFilter contains a compile, an evaluate and a finalize function.
A WriterContentFilter contains a compile, an evaluate, a finalize, a writer_attach, writer_compile, writer_evaluate, wrtier_detach, and writer_finalize functions.
A ContentFilteredTopic can be created with a dds::topic::Filter that has been created with the name that is registered with the participant.
A custom ContentFilter is used by RTI Connext at the following times during the life-time of a ContentFilteredTopic (the function called is shown in parenthesis).
A custom WriterContentFilter is used by RTI Connext at the following times during the life-time of a ContentFilteredTopic (the function called is shown in parenthesis).
The "compile" function is used to compile a filter expression and expression parameters. Please note that the term compile is intentionally loosely defined. It is up to the user to decide what this function should do and return.
When using a ContentFilter, the "evaluate" function is called each time a sample is received to determine if a sample should be filtered out and discarded.
When using a WriterContentFilter, the "evaluate" function is called each time a sample is written to determine if a sample should be filtered out and discarded. It is called for each DataReader for which the DataWriter is filtering and for which the writer_compile function set the ExpressionProperty.writer_side_filter_optimization to false.
The "finalize" function is called when an instance of the custom content filter is no longer needed. When this function is called, it is safe to free all resources used by this particular instance of the custom content filter.
The "writer_attach" function is called the first time that a DataWriter matches with a DataReader with the same ContentFilter. It will not be called for subsequent DataReaders that are using the same filter. This function is used to create some state required to perform filtering on the writer-side. It is entirely up to you, as the implementer of the filter, to decide if the filter requires this state.
The "writer_compile" function is called when a DataWriter matches with a DataReader with the same ContentFilter. It is called every time that the DataWriter matches a DataReader that is using the same filter as well as each time the DataWriter is notified that a DataReader's filter parameters have changed. This function will receive as an input a rti::core::Cookie which uniquely identifies the DataReader for which the function was invoked.
The "writer_evaluate" function is called every time that a DataWriter writes a new sample. Its purpose is to evaluate the sample for all the readers for which the DataWriter is performing writer-side filtering and return the sequence of rti::core::Cookie associated with the DataReaders whose filter pass the sample.
The "writer_detach" function is called when an instance of the custom content filter is no longer needed. When this function is called, it is safe to free all resources used by this particular instance of the custom content filter.
The "writer_return_loan" function is called to return the loan on the rti::core::CookieSeq provided by the writer_evaluate function.
The "writer_finalize" function will be called by Connext to notify the filter implementation that the DataWriter is no longer matching with a DataReader for which it was previously performing writer-side filtering. This will allow the filter to purge any state it was maintaining for the DataReader.
Assume that you have a type Foo.
Our filter will show how to enable the writer-side filter optimization for some readers and not for others. The ones with writer-side filter optimization will only pass samples where Foo.x == y where y is a value determined by an expression parameter, see the writer_evaluate function. Readers without the optimization turned on will pass all samples where Foo.x > 7, see the evaluate function. The filter will only be used to filter samples of type Foo.
The following #includes are needed for the examples on this page
The following is the definition of the WriterFilterData, the state that is created and returned by the writer_attach method:
And here is the declaration of our custom writer content filter. Notice, we are inheriting from rti::topic::WriterContentFilter. We could have inherited from rti::topic::ContentFilter or rti::topic::WriterContentFilterHelper here too to create other custom content filters.
Since we already know what the expression is (Foo.x > 7), we can simply return rti::topic::no_compile_data.
Below is the entire compile function.
The next step is to implement the evaluate function. The evaluate function receives no_compile_data because it is unecessary in this example. The function then evaluates the received sample against our filter expression and passes the sample if Foo.x > 7. Below is the entire evaluate function.
The next function to write is the finalize function. It is safe to free all resources used by this particular instance of the custom content filter that is allocated in compile. Because we did not create any resources in the compile function, we have nothing to do in the finalize. Below is the entire finalize function.
The writer_attach is used to create some state required to perform filtering on the writer-side. In our example filter, this state is kept as part of our custom filter class, ExampleWriterContentFilter, so we therefore simply return a reference to our writer data that will be used during the writer_compile and writer_evaluate functions.
The writer_compile function is called when a DataWriter matches with a DataReader with the same ContentFilter. In our case, we use the parameters to determine if we should turn on the writer-side filtering optimization. If parameters[0] == 1, then we store the provided rti::core::Cookie along with parameters[1] in our writer_filter_data to be accessed during the writer_evaluate function whenever we receive a new sample.
The writer_evaluate function receives our stored writer_filter_data and a sample to evaluate. We iterate through our (rti::core::Cookie, value) pairs and add any Cookie with a matching value of x to the sample's Foo.x to the rti::core::CookieSeq that we return. Any Cookie in this sequence then represents a DataReader to which this sample will be sent.
It is safe to free all resources used by this particular instance of the custom content filter that is allocated in writer_attach. Because we did not allocate any resources in the writer_attach function, there is nothing to release in the writer_finalize. Below is the entire writer_detach function.
RTI Connext uses the writer_return_loan function specified in the WriterContentFilter to indicate to the filter implementation that it has finished using the sequence of cookies returned by the filter�s writer_evaluate function. Your filter implementation should not free the memory associated with the cookie sequence before the writer_return_loan function is called. You can also create your custom content filter by inheriting from the rti::topic::WriterContentFilterHelper, which manages the DataReader CookieSeq for you. If you do that, then the writer_return_loan is implemented for you. Below is the entire writer_return_loan function.
The writer_finalize function specified in the WriterContentFilter will be called when the DataWriter no longer matches with a DataReader that was created with ContentFilteredTopic. This will allow the filter implementation to delete any state it was maintaining for the DataReader. Because we did not create any resources in the writer_compile function, we have nothing to do in the writer_finalize. Below is the entire writer_finalize function.
The first thing that an application needs to do when using a custom filter is to give it a name and wrap an instance of their custom filter with a rti::topic::CustomFilter. The CustomFilter class receives shared pointer to an instance of your custom filter and ensures that your filter does not go out-of-scope while it is being used. This means that you do not have to retain a reference to your filter throughout its lifetime, the CustomFilter class handles this detail for you.
After wrapping your content filter with the CustomFilter class, and before the custom filter can be used, it must be registered with RTI Connext:
When the filter is no longer needed, it can be unregistered from RTI Connext:
After the custom filter has been registered, you must create dds::topic::Filter that contain the filter expressions and parameters that will be used in your filter. You must give these Filters names that match the name with which the CustomFilter was registered in order to associate the two.
Next, create the readers that will be using the custom filter with dds::topic::ContentFilteredTopics that have been created with the Filters you created.
After that, everything is set up for you. Now, your readers will only receive samples matching the filter that you have set up for them.
A custom filter that is registered with a DomainParticipant can be looked up: