Cloning¶
In this section we are going to illustrate the concept of cloning a
multi_type_vector instance.
Cloning is probably only relevant when your multi_type_vector type
includes at least one element block whose type is either
noncopyable_element_block or
noncopyable_managed_element_block. These
blocks don’t allow copy construction; it would throw an
element_block_error if attempted. When you
call the clone() method,
however, you can create a copy of the original instance as if it was
copy-constructed provided that you implement a necessary template
specialization for cloning an element value stored in such a block.
In the example below, we are going to set up the code to allow cloning
of elements stored in an block whose type is
noncopyable_managed_element_block.
First, let’s define a value type that we don’t want to allow copying of:
/**
* Fictional class to store a stream of data from an external location.
*/
class stream_store
{
std::vector<uint8_t> m_buffer;
public:
stream_store() = default;
stream_store(std::vector<uint8_t> data) : m_buffer(std::move(data))
{
std::cout << "storing a buffer of size " << m_buffer.size() << std::endl;
}
stream_store(const stream_store&) = delete;
~stream_store()
{
std::cout << "disposing of the buffer of size " << m_buffer.size() << std::endl;
}
const std::vector<uint8_t>& get_buffer() const { return m_buffer; }
};
This fictional class stores a stream of bytes whose size can be
potentially very large that we don’t want to allow copying to avoid
potential performance bottleneck. So we’ll store them as pointers in a
block of type noncopyable_managed_element_block:
// static block ID for stream_store
constexpr mdds::mtv::element_t stream_store_id = mdds::mtv::element_type_user_start;
// block type for stream_store
using stream_store_block_type =
mdds::mtv::noncopyable_managed_element_block<stream_store_id, stream_store>;
// macro that defines for callbacks for pointer type
MDDS_MTV_DEFINE_ELEMENT_CALLBACKS_PTR(
stream_store,
stream_store_id,
nullptr,
stream_store_block_type
)
Once the element block type is defined, plug it into the traits type and
define the actual multi_type_vector type:
struct mtv_traits : mdds::mtv::default_traits
{
using block_funcs = mdds::mtv::element_block_funcs<stream_store_block_type>;
};
using mtv_type = mdds::mtv::soa::multi_type_vector<mtv_traits>;
We’ll also define a fictional function that fetches a stream of potentially large data from an external location:
/**
* Fictional function to fetch a stream of data from an external location.
*/
std::vector<uint8_t> fetch_buffer();
The content of this function is not very important, but the relevant piece is that it returns a sequence of bytes when called.
Next, we’ll define a template specialization for cloning a stream_store
instance. Since we are storing its instances as pointers, we need to
specialize for the stream_store* type:
namespace mdds { namespace mtv {
template<>
struct clone_value<stream_store*>
{
stream_store* operator()(const stream_store* src) const
{
return new stream_store(src->get_buffer());
}
};
}}
The specialization must be a struct, must be in the mdds::mtv
namespace, and must have a public method whose signature is T
operator()(const T) const where the T is the type that it is
specialized for.
Since we have all necessary pieces defined, let’s instantiate our
multi_type_vector instance and populate it:
mtv_type store;
store.push_back(new stream_store(fetch_buffer()));
store.push_back(new stream_store(fetch_buffer()));
store.push_back(new stream_store(fetch_buffer()));
store.push_back(new stream_store(fetch_buffer()));
store.push_back(new stream_store(fetch_buffer()));
We are storing heap-allocated instances directly in the container, which
will manage their life cycles. Let’s clone this instance by calling the
clone() method:
std::cout << "cloning the container..." << std::endl;
auto cloned = store.clone();
std::cout << "done cloning" << std::endl;
This will internally call the clone_value specialization we defined
earlier to perform cloning. You will see output similar to the following
when executing this code:
storing a buffer of size 736421
storing a buffer of size 273794
storing a buffer of size 444271
storing a buffer of size 1044677
storing a buffer of size 492239
cloning the container...
storing a buffer of size 736421
storing a buffer of size 273794
storing a buffer of size 444271
storing a buffer of size 1044677
storing a buffer of size 492239
done cloning
disposing of the buffer of size 736421
disposing of the buffer of size 273794
disposing of the buffer of size 444271
disposing of the buffer of size 1044677
disposing of the buffer of size 492239
disposing of the buffer of size 736421
disposing of the buffer of size 273794
disposing of the buffer of size 444271
disposing of the buffer of size 1044677
disposing of the buffer of size 492239
The output indicates that cloning did clone all stored stream_store
instances, and all of the stored instances were properly disposed of
when the two multi_type_vector container instances storing them were
destroyed.