Resolve "feat(core): add generic container classes"
Description
Implements a generic templated data container class, aka IContainer
, that allows code factorization from all containers. STL API from the corresponding STL container classes is exposed and the inherited containers, such Composite
and Vector
can be seen as a real std::vector
and std::map
from outside (which they indeed are !), while keeping to be data objects, that can be used in XML, that can be serialized, etc..
Since it is a part of #862 (closed), some containers (ActivitySet
, CameraSet
, SeriesSet
) have been also implemented. Although not used yet, you can still take a look at them, as CameraSet
uses a more complex std::vector<std::pair<Camera::sptr, Matrix4::sptr>>
while ActivitySet
and SeriesSet
use a Boost Multi-index, which allows to define a set
which is sequenced like a vector
.
Usage
The best open Composite
or Vector
and IContainerTest
code, but basically, all you have to do if you need a specific data object container class, is to inherits from sight::data::IContainer<XXX>
, with XXX
being your STL container type (or boost STL compatible container, like Multi-index !). As an example:
#include "data/IContainer.hpp"
class DATA_CLASS_API Vector final : public IContainer<std::vector<Object::sptr> >
{
...
};
You can then use it like a real std::vectorObject::sptr:
auto vector = sight::data::Vector::New();
// 99.9% of STL API is available
vector.reserve(2)
vector.push_back(sight::data::Integer::New(1));
vector.push_back(vector.front())
vector[1] = sight::data::Integer::New(2)
...
// Initializer list / assignment operators
vector = {sight::data::Integer::New(1), sight::data::Integer::New(2), sight::data::Integer::New(3)};
// iterators are supported
for(const auto& element ; *vector)
{
std::cout << element->getValue() << std::endl;
}
Notifier
There is also a generic Notifier
which replace various "helpers", that were used to send signals when adding / removing objects in the container. It uses RAII mechanism to send the right signals when it is deleted.
To use them, simply call the generic get_notifier()
function form a container to get a Notifier
instance, and perform operation on the container. The notifier will take a snapshot of the content and compare it with the current container upon destruction. Signals will be fired if elements have been added or removed or changed (in case of a map like container). Short example:
auto composite = sight::data::Composite::New();
{
auto notifier = composite->get_notifier();
// Now modification to `composite` will be notified to whatever is connected to `composite` signals
composite->insert({"beast", sight::data::Integer::New(666)});
...
// Signals are sent when notifier is destroyed, like outside this scope...
}
Advanced usage
Writing specific code for specific container
Sometimes, it is useful to know if an object is a container and if yes, from which kind it is. Some template matching functions have been added to core/tools/compare.hpp
:
#include <core/tools/compare.hpp>
template<typename T>
void my_function(const T& truc)
{
...
if constexpr(core::tools::is_map_like<T>::value)
{
truc.insert({"maman", value});
}
else if constexpr(core::tools::is_container<T>::value)
{
truc.insert(value);
}
else
{
....
}
}
IContainer
Generic container without inheriting from If you don't want the IContainer<XXX>
inheritance because you don't want to be a sight::data::Object
, but still want to act like a XXX
, you could inherit directly from ContainerWrapper<XXX>
:
class MySimpleContainer : public ContainerWrapper<std::set<std::string>>
{
...
};
MySimpleContainer a;
a.insert("Le petit chaton bleu est très malade.");
...
See #885 (closed) for some details
Closes #885 (closed)
How to test it?
Look the code and launch unit tests (especially datatest)