Specifying custom types in element blocks¶
There are times when you need to store a set of user-defined types in multi_type_vector.
That is what we are going to talk about in this section.
First, let’s include the header:
#include <mdds/multi_type_vector.hpp>
then proceed to define some constant values to use as element types. We are going to define three custom value types, so we need three element types defined:
constexpr mdds::mtv::element_t custom_value1_type = mdds::mtv::element_type_user_start;
constexpr mdds::mtv::element_t custom_value2_type = mdds::mtv::element_type_user_start + 1;
constexpr mdds::mtv::element_t custom_value3_type = mdds::mtv::element_type_user_start + 2;
Here, you need to ensure that the values used will not collide with the values
that may be used for the standard value types. The best way to ensure that is
to assign the values that are greater than or equal to element_type_user_start
as the code above does. Values less than element_type_user_start
are reserved for use either for the standard value types or any other internal uses
in the future.
Now, let’s define the first two custom value types, and their respective block types:
struct custom_value1 {};
struct custom_value2 {};
using custom_value1_block = mdds::mtv::default_element_block<custom_value1_type, custom_value1>;
using custom_value2_block = mdds::mtv::default_element_block<custom_value2_type, custom_value2>;
Here, we are using the default_element_block as the basis
to define their block types. At minimum, you need to specify the element type constant
and the value type as its template arguments. There is a third optional template
argument you can specify which will become the underlying storage type. By
default, delayed_delete_vector is used when the third
argument is not given. But you can specify other types such as std::vector
or std::deque instead, or any other types that have similar interfaces
to std::vector.
Once the block types are defined, it’s time to define callback functions for them.
This should be as simple as using the MDDS_MTV_DEFINE_ELEMENT_CALLBACKS with
all necessary parameters provided:
MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(custom_value1, custom_value1_type, custom_value1{}, custom_value1_block)
MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(custom_value2, custom_value2_type, custom_value2{}, custom_value2_block)
Our third type is defined in a namespace ns, and its associated block type is
also defined in the same namespace. One thing to keep in mind is that, when the
custom type is defined in a namespace, its callback functions must also be defined
in the same namespace in order for them to be discovered per argument dependent lookup
during overload resolution. This means that you must place the macro that defines
the callback functions in the same namespace as the namespace that encompasses the
original value type:
namespace ns {
struct custom_value3 {};
using custom_value3_block = mdds::mtv::default_element_block<custom_value3_type, custom_value3>;
// This macro MUST be in the same namespace as that of the value type, in order for
// argument-dependent lookup to work properly during overload resolution.
MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(custom_value3, custom_value2_type, custom_value3{}, custom_value3_block)
} // namespace ns
Warning
If the original value type is defined inside a namespace, its associated callback functions must also be defined in the same namespace, due to the way argument dependent lookup works during overload resolution.
The next step is to define a trait type that specifies these block types. The
easiest way is to have your trait inherit from mdds::mtv::default_traits
and overwrite the block_funcs static member
type with an instance of mdds::mtv::element_block_funcs with one or
more block types specified as its template arguments:
struct my_custom_traits : public mdds::mtv::default_traits
{
using block_funcs = mdds::mtv::element_block_funcs<
custom_value1_block, custom_value2_block, ns::custom_value3_block>;
};
Now we are ready to define the final multi_type_vector type
with the trait we just defined:
using mtv_type = mdds::multi_type_vector<my_custom_traits>;
And that’s it! With this in place, you can write a code like the following:
mtv_type con{}; // initialize it as empty container.
// Push three values of different types to the end.
con.push_back(custom_value1{});
con.push_back(custom_value2{});
con.push_back(ns::custom_value3{});
auto v1 = con.get<custom_value1>(0);
auto v2 = con.get<custom_value2>(1);
auto v3 = con.get<ns::custom_value3>(2);
std::cout << "is this custom_value1? " << std::is_same_v<decltype(v1), custom_value1> << std::endl;
std::cout << "is this custom_value2? " << std::is_same_v<decltype(v2), custom_value2> << std::endl;
std::cout << "is this ns::custom_value3? " << std::is_same_v<decltype(v3), ns::custom_value3> << std::endl;
to put some values of the custom types into your container and accessing them. This code should generate the following output:
is this custom_value1? 1
is this custom_value2? 1
is this ns::custom_value3? 1