An STL vector of pointers that by default owns its pointed-to elements. More...
#include "AthContainers/exceptions.h"
#include "AthContainers/OwnershipPolicy.h"
#include "AthContainers/IndexTrackingPolicy.h"
#include "AthContainers/AuxVectorBase.h"
#include "AthContainers/tools/DVLNoBase.h"
#include "AthContainers/tools/DVLInfo.h"
#include "AthContainers/tools/DVLCast.h"
#include "AthContainers/tools/DVLIterator.h"
#include "AthContainers/tools/DVL_iter_swap.h"
#include "AthContainers/tools/DVL_algorithms.h"
#include "AthContainers/tools/ElementProxy.h"
#include "AthContainers/tools/IsMostDerivedFlag.h"
#include "AthLinks/tools/selection_ns.h"
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>
#include <boost/iterator/iterator_adaptor.hpp>
#include <vector>
#include <typeinfo>
#include <functional>
#include <iostream>
#include <algorithm>
#include <stdexcept>
#include <iterator>
#include "SGTools/BaseInfo.h"
#include "AthContainers/ClassName.h"
#include "AthContainers/tools/DVLDataBucket.h"
#include "SGTools/DataBucketTraitFwd.h"
#include "AthContainers/DataVector.icc"
Go to the source code of this file.
Classes | |
struct | DataVectorBase< T > |
Derivation information for DataVector . More... | |
class | DataVector< T, BASE > |
Derived DataVector<T> . More... | |
class | DataVector< T, DataModel_detail::NoBase > |
Base specialization for DataVector<T> . More... | |
class | DataVector< T, BASE > |
Derived DataVector<T> . More... | |
class | ClassName< DataVector< T > > |
Specialization of ClassName for DataVector . More... | |
struct | SG::DataBucketTrait< DataVector< T >, U > |
Metafunction to find the proper DataBucket class for T . More... | |
Namespaces | |
namespace | SG |
Constructor from a payload object. | |
Defines | |
#define | HAVE_CONSTDATAVECTOR |
#define | DATAVECTOR_BASE(T, BASE) |
Declare base class info to DataVector . Single, non-virtual derivation. | |
#define | DATAVECTOR_BASE_FWD(T, BASE) |
Version of DATAVECTOR_BASE that can be used in forward declarations. | |
#define | DATAVECTOR_VIRTBASES1(T, B1) |
Declare base class info to DataVector . Single, virtual derivation. | |
#define | DATAVECTOR_VIRTBASES1_FWD(T, B1) |
Version of DATAVECTOR_VIRTBASES1 that can be used in forward declarations. | |
#define | DATAVECTOR_VIRTBASES2(T, B1, B2) |
Declare base class info to DataVector . Multiple derivation. | |
#define | DATAVECTOR_VIRTBASES2_FWD(T, B1, B2) |
Version of DATAVECTOR_VIRTBASES2 that can be used in forward declarations. | |
#define | DATAVECTOR_VIRTBASES3(T, B1, B2, B3) |
Declare base class info to DataVector . Multiple derivation. | |
#define | DATAVECTOR_VIRTBASES3_FWD(T, B1, B2, B3) |
Version of DATAVECTOR_VIRTBASES3 that can be used in forward declarations. | |
Functions | |
template<class DV > | |
void | test2_assignelement1 () |
template<class DV > | |
void | test2_assignelement2 () |
template<class T > | |
bool | operator== (const DataVector< T > &a, const DataVector< T > &b) |
Vector equality comparison. | |
template<class T > | |
bool | operator!= (const DataVector< T > &a, const DataVector< T > &b) |
Based on operator==. | |
template<class T > | |
bool | operator< (const DataVector< T > &a, const DataVector< T > &b) |
Vector ordering relation. | |
template<class T > | |
bool | operator> (const DataVector< T > &a, const DataVector< T > &b) |
Based on operator<. | |
template<class T > | |
bool | operator<= (const DataVector< T > &a, const DataVector< T > &b) |
Based on operator<. | |
template<class T > | |
bool | operator>= (const DataVector< T > &a, const DataVector< T > &b) |
Based on operator<. | |
template<class T > | |
void | swap (DataVector< T > &a, DataVector< T > &b) |
See DataVector<T, BASE>::swap() . |
An STL vector of pointers that by default owns its pointed-to elements.
Introduction ============
A DataVector<T>
acts like a std::vector<T*>
, except that it can optionally manage the memory that it contains. The constructors take an (optional) extra argument, which can be either SG::OWN_ELEMENTS
or SG::VIEW_ELEMENTS
(defaulting to SG::OWN_ELEMENTS
, except for a copy constructor). This tells whether the DataVector
owns its contained elements or not.
Ownership issues ================
If a DataVector
owns its elements, then they are deleted when the container itself is. Further, they are deleted by actions which erase elements from the container (i.e, erase()
, pop_back()
). A replacement (such as v[0] = new T
) will result in the old element being deleted and the container taking ownership of the new element. It is an error to assign directly between two owning containers (v1[0] = v2[0]
). To remove an element from a container and acquire ownership, use swapElement
.
Beware of ownership issues when you modify a DataVector
. Obviously you should not delete explicitly a DataVector
element. A DataVector
should never have two elements pointing to the same object. This may seem obvious but certain std algorithms (e.g. remove_if
) may leave a DataVector
with two copies of the same element in the "left-over" range. To avoid a crash when clearing the vector (e.g. in the destructor we have introduced a helper function that searches and removes duplicates in the DataVector
. This is used by the destructor by clear()
and by erase(first, last)
. As this may change in the future to improve performance, do not rely on this functionality and do avoid introducing duplicated elements in a DataVector
.
All these cautions do not apply when a DataVector
it is created with the flag SG::VIEW_ELEMENTS
(see enum OwnershipPolicy
) and hence does not own its elements. This is typically used to have DataVector
elements allocated by DataPool
. Otherwise consider the cleaner alternative of using a vector<T*>
.
The interface for DataVector
should be mostly compatible with that of std::vector
. There are a few differences which should not make much difference in practice. For example, methods which would return a reference return a proxy object instead. Also value_type
is used instead of const_reference
; this is justified by the fact that the elements are always pointers.
Note that algorithms which modify their range may not work correctly if the container owns its contents. Specializations that work properly for DataVector
are available for some algorithms. These include:
std::sort
std::stable_sort
std::partial_sort
std::remove
std::remove_if
std::unique
std::reverse
std::rotate
std::random_shuffle
std::partition
std::stable_partition
Alternately, for sort()
, the sort()
methods defined in DataVector
may be used. Or do the sorting in a view DataVector
or a plain std::vector
.
There are a few other additions to the standard std::vector
interface.
stdcont
may be used to get access to the underlying std::vector
representation.PtrVector
is the type of the underlying std::vector
. BaseContainer
is a synonym for this.swapElement
may be used to get ownership of an element back from a DataVector
.sort
provides an interface to std::sort
that works even if the container owns its contents.ownPolicy
returns the ownership policy of the container.clear()
is provided that takes as an argument a new ownership policy for the container. This is the only way to change the ownership policy, save for swapping or moving the container.Note that since DataVector<T>
has an element type of T*
, it is not possible to directly insert a const T*
. If you want to do that, see ConstDataVector
. (In some cases, such as if the destination container is not being recorded in StoreGate, it may be more appropriate to simply use a std::vector<const T*>
.) Don't just use a const_cast!
Inheritance ===========
DataVector's
may inherit from one another. If you have class D
which derives from class B
, you can set things up so that DataVector<D>
derives from DataVector<B>
. This allows you do to the same sort of conversions on the DataVector's
as on the element pointers themselves. The key to doing this is to add the declaration
DATAVECTOR_BASE (D, B);
before using DataVector<D>
. A few caveats about doing this. The pointers are actually stored in the base DataVector
instance, and the type that stdcont
returns will reflect this. For example, in the example given above, DataVector<D>::stdcont()
will return a reference to std::vector<B*>. Second, in order to preserve the invariant that a DataVector<D>
contains only elements that actually derive from D
, while at the same time not requiring that the contained objects be polymorphic, there is a restriction that you cannot insert into a DataVector
if you're not referring to it as the most derived type (even if such an insertion would not actually break the invariant). This is implemented as a runtime check.
Example:
DataVector<D> vd; vd.push_back (new D); // This is ok. vd.push_back (new B); // This will give a compilation error (it would break the invariant). DataVector<B>& vb = vd; vb.push_back (new B); // This will give a run-time error (it breaks the invariant). vb.push_back (new D); // This will also give a run-time error. (It's actually ok, but there's no good way to distinguish it from the previous case.)
Note also this (related to a common atlas idiom). If we have the above, and also:
class B_Vector : public DataVector<B> { ... }; class D_Vector : public DataVector<D> { ... };
Then a D_Vector
will be convertible to a DataVector<B>, but _not_ to a B_Vector
.
Multiple and virtual inheritance are also supported. In this case, use DATAVECTOR_VIRTBASES
n (where n is 1, 2, or 3) instead of DATAVECTOR_BASE
. Example: Given:
class M { ... }; class N : virtual public M { ... }; class O : virtual public M { ... }; class P : virtual public N, virtual public O { ... };
declare this with
DATAVECTOR_VIRTBASES1(N, M); DATAVECTOR_VIRTBASES1(O, M); DATAVECTOR_VIRTBASES2(P, N, O);
There is a restriction that there must be a unique base class that does not derive from anything else. For example, the diamond configuration above is ok, but this would not be:
class L { ... }; class M { ... }; class N : virtual public M, virtual public L { ... }; class O : virtual public M { ... }; class P : virtual public N, virtual public O { ... }; DATAVECTOR_VIRTBASES2(N, M, L); DATAVECTOR_VIRTBASES1(O, M); DATAVECTOR_VIRTBASES2(P, N, O);
Note, however, that you don't have to tell DataVector
about the complete hierarchy; leaving the L
out of DATAVECTOR_VIRTBASES
would work (you just wouldn't be able to convert to DataVector<L>
).
If you use DATAVECTOR_VIRTBASES
, there is an additional time penalty to retrieve elements from the collection. This does not apply for DATAVECTOR_BASES
.
All applicable DATAVECTOR_*
macros must be visible at the point at which a DataVector
is instantiated. A confusing compilation error is likely to result otherwise. Note that this means that if you have the DATAVECTOR_*
macros within a container header file, then the header for the derived container must include the header for the base container. Be alert to this when converting existing code to use the inheritance scheme. For example, if class D2 derives from D which derives from B:
BVec.h:
#include "B.h" #include "DataVector.h" typedef DataVector<B> BVec;
DVec.h:
#include "D.h" #include "DataVector.h" DATAVECTOR_BASE(D,B); typedef DataVector<D> DVec;
D2Vec.h:
#include "D2.h" #include "DataVector.h" #include "DVec.h" // This is required DATAVECTOR_BASE(D2,D); typedef DataVector<D2> DVec;
Using DATAVECTOR_BASE
will also set up the corresponding SG::BaseInfo
definitions, both for the vectors themselves and for the contained objects.
Auxiliary data ==============
A DataVector
may also have `auxiliary data' associated with it. This is a set of name/value pairs that can be attached to each element of the vector. These data are stored in vectors that exist parallel to the DataVector
; the exact way in which this managed is hidden behind an abstract interface.
We'll start with an example, and then go into more detail.
class MyClass : public SG::AuxElement // Derive from AuxElement to allow for aux data { public: // A getter for an aux data item. float foo() const { static ConstAccessor<float> acc ("foo"); return acc(*this); } // A setter for an aux data item. void foo(float x) { static Accessor<float> acc ("foo"); acc(*this) = x; } }; ... // Create the vector and associate a store. DataVector<MyClass>* v = new DataVector<MyClass>; SG::AuxStoreInternal* store = new SG::AuxStoreInternal; v->setStore (store); // Add an item to the vector and set aux data on it. v->push_back (new MyClass); v[0]->foo() = 3; // Add a user-defined aux data item. MyClass::Accessor<int> x ("x"); x(*v[0]) = 10; // Add a standalone store to an object. MyClass* c = new MyClass; c->makePrivateStore(); c->foo() = 4; c->push_back (c); // Aux data will be transferred to the container.
In order to be able store auxiliary data in a container three things must hold. First, the container's payload type must derive from SG::AuxElement
. Second, the container must have index tracking enabled. Third, the container must have an associated auxiliary data store. More about these below.
SG::AuxElement --------------
A type which may have associated auxiliary data must derive from the base class SG::AuxElement
. This does a several things.
First, in order to be able to find auxiliary data from a pointer to a container element, the element must be able to know both the container that it's a member of and its index within that container. SG::AuxElement
makes this information available with the container()
and index()
methods. This information is maintained as long as the container has index tracking enabled (see below).
Second, it provides an interface for getting/setting auxiliary data. The recommended way of doing this is through the nested Accessor
and ConstAccessor
classes; these allows caching the translation from attribute names to the internal representation. The difference between these two is that ConstAccessor
allows only const access to the element, while Accessor
, which derives from it, allows non-const access as well. Create an Accessor
or ConstAccessor
object with the data type (which can be anything that can be used within a std::vector
) and a name. For Accessor
, the resulting object can then be used both as an rvalue and a lvalue; for ConstAccessor
, it can be used only as an rvalue.
MyClass* c = ...; MyClass::Accessor<double> y ("y"); y(*c) = 4; // assign attribute. return y(*c); // read attribute.
A given name must always be used with the same type, otherwise an exception will be thrown. In the case that you want to use the same name for different types in different classes, the name may be qualified with an optional class name:
MyClass::ConstAccessor<double> y ("y", "MyClass");
If you have some auxiliary data items that are to be regarded as members of a class, it is recommended to define a setter and getter as in the example above.
If you need to operate on a particular auxiliary data item for all elements in a container, it will be more efficient to access the auxiliary data directly by index, rather than through the elements. This can be done like:
DataVector<MyClass>* v = ...; MyClass::ConstAccesor<float> x ("x"); size_t sz = v->size(); for (size_t i = 0; i < sz; i++) do_something (x(*v, i));
Accessor
and ConstAccessor
also have getDataArray
methods to directly return a pointer to the start of the auxiliary data array.
It is also possible to use the auxdata
method to access auxiliary data directly. However, it is not recommended to do this inside a loop or anywhere where performance is important.
c->auxdata<int> ("aux") = 5; // set return c->auxdata<int> ("aux"); // retrieve
Decorations -----------
In addition, sometimes one wants to add additional auxiliary data to an existing, const container; this is called `decorating' it. For example, you might want to retrieve a const container from StoreGate, run some algorithm on it, and attach additional data to the object that can be accessed by downstream algorithms or written to a file. To support this, there is a Decorator
object analogous to Accessor
and ConstAccessor
. The difference is that Decorator
can operate on a const object and return a non-const reference.
To prevent modifying existing data, the contents of a container may be locked with the lock()
call; this happens automatically when the container is made const with StoreGateSvc::setConst
. Once a container is locked, Decorator
will only succeed if either the variable does not yet exist (in which case it is created and marked as a decoration) or it is marked as a decoration.
Calling clearDecorations
will erase all variables marked as decorations, restoring the state back to where it was when lock
was called.
An auxdecor
method is also available, analogous to auxdata.
Index tracking --------------
Recall that a DataVector
either owns its elements or not. By default, a DataVector
that owns its elements will update the container and index fields of the elements that it contains, while a view DataVector
will not.
This rule does not always suffice, though. In particular, a DataVector
that gets filled with elements from a DataPool
does not own its elements but should track indices. For this reason, one can specify an index tracking policy separate from the ownership policy. Where DataVector
takes an ownership policy, it can also take as the next argument an index tracking policy. This policy defaults to SG::DEFAULT_TRACK_INDICES
, which means to set the indexing policy based on the ownership policy. But it can also be set to SG::ALWAYS_TRACK_INDICES
or SG::NEVER_TRACK_INDICES
to override this.
The current state of index tracking for a DataVector
is returned by the trackIndices
method. Like the ownership policy, it may be changed with the clear
method.
Auxiliary store ---------------
DataVector
does not itself manage the storage for auxiliary data. Instead, this is done by a separate _auxiliary store_ object. This store object implements either the interface SG::IConstAuxStore
or SG::IAuxStore
(which derives from it). The first of these provides operations needed to read data from the store, while the second adds operations needed to add to and modify the data in the store.
When you are create a DataVector
object that is to have auxiliary data, you will also need to create the store object. A generic store object, which manages dynamic auxiliary data, is available, SG::AuxStoreInternal
. There may also be specialized store implementations for particular data classes. Store implementations also exist that are specialized for use in root; in that case, the data are managed as part of a root tree, and the store object manages access to it.
DataVector<MyClass>* v = new DataVector<MyClass>; CHECK( evtStore->record (v, "mykey") ); SG::AuxStoreInternal* store = new SG::AuxStoreInternal; CHECK( evtStore->record (store, "mykeyAux.") ); v->setStore (store);
You can only set a store if the container has index tracking enabled; otherwise, an exception will be thrown.
You should not have to explicitly deal with the auxiliary store for objects that are read from a file. The I/O system is responsible for getting the store association correct in that case.
Standalone objects ------------------
Normally, an element can only have associated auxiliary data if it is a member of a container; otherwise, there is no store in which to store the auxiliary data. However, it is possible to request that a store be created for an individual element not part of a container by calling the method makePrivateStore
. You can then add auxiliary data to the object:
MyClass* c = new MyClass; c->makePrivateStore(); MyClass::Accessor<int> x ("x"); x(*c) = 5;
If the element is then added to a container, the auxiliary data will be copied to the container's store, and the private store the was associated with the element will be released. The fact that there was a private store is remembered, however; if the element is later removed from the container (in a way that doesn't delete the element), the private store will be remade and the auxiliary data copied back from the container to the private store.
makePrivateStore
can also take an argument. If provided, auxiliary data will be copied from it. This can be used to properly implement copy constructors:
class MyStandaloneClass : public SG::AuxElement { public: MyStandaloneClass() { this->makePrivateStore(); } MyStandaloneClass (const MyStandaloneClass& other) SG::AuxElement (other) { this->makePrivateStore (other); }
As a shorthand, one can use the wrapper class SG::AuxElementComplete
. This will add constructors that automatically make a private store:
typedef SG::AuxElementComplete<MyClass> MyStandaloneClass;
A standalone object also has setStore
methods that can be used to associate an external store with a since object, in the same manner as for containers.
#define DATAVECTOR_BASE | ( | T, | |||
BASE | ) |
DATAVECTOR_BASE_FWD(T, BASE); \ template struct DataVector_detail::DVLEltBaseInit<T>
Declare base class info to DataVector
. Single, non-virtual derivation.
DATAVECTOR_BASE(D, B)
says that D
derives non-virtually from B
.
This macro creates an appropriate specialization of DataVectorBase
.
#define DATAVECTOR_BASE_FWD | ( | T, | |||
BASE | ) |
template <> struct DataVectorBase<T> \ { typedef DataVector<BASE> Base; }; \ SG_BASE(DataVector<T>, DataVector<BASE>)
Version of DATAVECTOR_BASE
that can be used in forward declarations.
#define DATAVECTOR_VIRTBASES1 | ( | T, | |||
B1 | ) |
DATAVECTOR_VIRTBASES1_FWD(T, B1); \ template struct DataVector_detail::DVLEltBaseInit<T>
Declare base class info to DataVector
. Single, virtual derivation.
DATAVECTOR_VIRTBASES1(D, B1)
says that D
derives virtually from B1
.
This macro creates an appropriate specialization of DataVectorBase
.
#define DATAVECTOR_VIRTBASES1_FWD | ( | T, | |||
B1 | ) |
template <> struct DataVectorBase<T> \ { typedef DataVector_detail::VirtBases<B1> Base; }; \ SG_BASES1(DataVector<T>, SG_VIRTUAL(DataVector<B1>))
Version of DATAVECTOR_VIRTBASES1
that can be used in forward declarations.
#define DATAVECTOR_VIRTBASES2 | ( | T, | |||
B1, | |||||
B2 | ) |
DATAVECTOR_VIRTBASES2_FWD(T, B1, B2); \ template struct DataVector_detail::DVLEltBaseInit<T>
Declare base class info to DataVector
. Multiple derivation.
DATAVECTOR_VIRTBASES2(D, B1, B2)
says that D
derives from both B1
and B2
.
This macro creates an appropriate specialization of DataVectorBase
.
#define DATAVECTOR_VIRTBASES2_FWD | ( | T, | |||
B1, | |||||
B2 | ) |
template <> struct DataVectorBase<T> \ { typedef DataVector_detail::VirtBases<B1, B2> Base; }; \ SG_BASES2(DataVector<T>, SG_VIRTUAL(DataVector<B1>), \ SG_VIRTUAL(DataVector<B2>))
Version of DATAVECTOR_VIRTBASES2
that can be used in forward declarations.
#define DATAVECTOR_VIRTBASES3 | ( | T, | |||
B1, | |||||
B2, | |||||
B3 | ) |
DATAVECTOR_VIRTBASES3_FWD(T, B1, B2, B3); \ template struct DataVector_detail::DVLEltBaseInit<T>
Declare base class info to DataVector
. Multiple derivation.
DATAVECTOR_VIRTBASES3(D, B1, B2, B3)
says that D
derives from all of B1
, B2
, and B3
.
This macro creates an appropriate specialization of DataVectorBase
.
#define DATAVECTOR_VIRTBASES3_FWD | ( | T, | |||
B1, | |||||
B2, | |||||
B3 | ) |
template <> struct DataVectorBase<T> \ { typedef DataVector_detail::VirtBases<B1, B2, B3> Base; }; \ SG_BASES3(DataVector<T>, SG_VIRTUAL(DataVector<B1>), \ SG_VIRTUAL(DataVector<B2>), \ SG_VIRTUAL(DataVector<B3>))
Version of DATAVECTOR_VIRTBASES3
that can be used in forward declarations.
bool operator< | ( | const DataVector< T > & | a, | |
const DataVector< T > & | b | |||
) | [inline] |
Vector ordering relation.
a | A DataVector . | |
b | A DataVector of the same type as x. |
This is a total ordering relation. It is linear in the size of the vectors. Comparisons are done on the pointer values of the elements.
See std::lexicographical_compare()
for how the determination is made.
bool operator== | ( | const DataVector< T > & | a, | |
const DataVector< T > & | b | |||
) | [inline] |
Vector equality comparison.
a | A DataVector . | |
b | A DataVector of the same type as x. |
This is an equivalence relation. It is linear in the size of the vectors. Vectors are considered equivalent if their sizes are equal, and if corresponding elements compare equal.