Files CCore/inc/Array.h CCore/src/Array.cpp
Subfolders CCore/inc/array CCore/src/array
There are several types, which are used to specify the constructor behavior.
/* words */ enum DoBuildType { DoBuild }; enum DoReserveType { DoReserve };
DoBuild and DoReserve are words. They designate the build and the reserve variant of the constructor respectively.
template <int Sw> class DoSomething { ulen len; ulen maxlen; public: explicit DoSomething(ulen len); // maxlen == len DoSomething(ulen len,ulen maxlen); // maxlen >= len ulen getLen() const { return len; } ulen getMaxLen() const { return maxlen; } }; typedef DoSomething<1> DoRaw; typedef DoSomething<2> DoFill; typedef DoSomething<3> DoCopy; typedef DoSomething<4> DoCast; typedef DoSomething<5> DoSwap; typedef DoSomething<6> DoCreate;
DoRaw, DoFill, DoCopy, DoCast, DoSwap, DoCreate are not words, but Small Data Types. They designate the correspondent variant of constructor and carry two parameters: the array length and the reserved length. When the array is being created, the reserved length determines the memory space to be allocated, and the length is the number of elements to be created. An exception is thrown, if len is greater than maxlen.
Array is a container, where multiple items comprise a continuous range. The simplest such "container" is the embedded type "array of" T[Len]. Unfortunately, this type lacks many abilities, required in applications, so we need a variety of Arrays. We don't use STL vector, because it also has many deficiencies. CCore Arrays use the CCore heap with its extra functionality. They also customizable using an Algorithm Package of array algorithms.
CCore Arrays implement Range Access Interface. They also provide the index access to elements through the overloaded operator [] and the checked index access with the method at().
Constant CCore Arrays give constant access to its elements.
Allocation errors and index check errors throw exceptions. Default constructors are no-throw.
The method apply() applies the functor, given by a Functor Init, to array elements. If the array is constant, then element references are constant too. The variant apply_const() also enforces constantness.
The method applyReverse() applies the functor, given by a Functor Init, to array elements in the reverse order. If the array is constant, then element references are constant too. The variant applyReverse_const() also enforces constantness.
Arrays can be used with most types, there is no hard restrictions on type properties. The only strong requirement is: the type destructor must be no-throw. Some operations, however, require additional type properties. For example, to use cloneTo() methods the type must be copyable.
All CCore Arrays available through the header CCore/inc/Array.h.
TempArray is the simplest of Arrays. It is applicable only to POD types. This container combines stack storage and dynamic storage to speed up the array construction/destruction for a short lengths. Use this Array if you need a temporary buffer with expected short length in the most cases.
template <class T,ulen StackLen>
class TempArray : NoCopy
{
private:
T *ptr;
ulen len;
T buf[StackLen];
public:
// constructors
TempArray() noexcept;
explicit TempArray(ulen len);
~TempArray();
// methods
void provide(ulen len);
void reset(ulen len);
bool extend(ulen len);
// range access
T * getPtr() { return ptr; }
const T * getPtr() const { return ptr; }
const T * getPtr_const() const { return ptr; }
ulen getLen() const { return len; }
// index access
T & operator [] (ulen index);
const T & operator [] (ulen index) const;
T & at(ulen index);
const T & at(ulen index) const;
// apply
template <class FuncInit>
void apply(FuncInit func_init);
template <class FuncInit>
void apply(FuncInit func_init) const;
template <class FuncInit>
void apply_const(FuncInit func_init) const;
template <class FuncInit>
void applyReverse(FuncInit func_init);
template <class FuncInit>
void applyReverse(FuncInit func_init) const;
template <class FuncInit>
void applyReverse_const(FuncInit func_init) const;
};
If the array length is not greater than StackLen, then TempArray uses the internal buffer. Otherwise, the space for elements is dynamically allocated.
provide() ensures the array has at least the given length, reallocating it if required. The previous array content may be lost. The new content is not initialized.
reset() recreates the array with the given length, reallocating it if required. The previous array content may be lost. The new content is not initialized.
extend() extends the array up to the given length. If len is less or equal than the current array length, the method does nothing and returns false. Otherwise it extends the array, preserving the current content, and returns true. The new content is not initialized. An exception is thrown if there is no memory.
Other methods are standard array methods.
SimpleArray is another "simple array". It is applicable to (almost) any types.
The second template argument of SimpleArray is an Algorithm Package of array algorithms. SimpleArray uses only two of them: Create_default() and Destroy(). See below about array algorithms.
template <class T,class Algo=ArrayAlgo<T> >
class SimpleArray : NoCopy
{
T *ptr;
ulen len;
public:
// constructors
SimpleArray() noexcept;
explicit SimpleArray(ulen len);
~SimpleArray();
// std move
SimpleArray(SimpleArray<T,Algo> &&obj) noexcept;
SimpleArray<T,Algo> & operator = (SimpleArray<T,Algo> &&obj) noexcept;
// range access
T * getPtr() { return ptr; }
const T * getPtr() const { return ptr; }
const T * getPtr_const() const { return ptr; }
ulen getLen() const { return len; }
// index access
T & operator [] (ulen index);
const T & operator [] (ulen index) const;
T & at(ulen index);
const T & at(ulen index) const;
// apply
template <class FuncInit>
void apply(FuncInit func_init);
template <class FuncInit>
void apply(FuncInit func_init) const;
template <class FuncInit>
void apply_const(FuncInit func_init) const;
template <class FuncInit>
void applyReverse(FuncInit func_init);
template <class FuncInit>
void applyReverse(FuncInit func_init) const;
template <class FuncInit>
void applyReverse_const(FuncInit func_init) const;
// swap/move objects
void objSwap(SimpleArray<T,Algo> &obj);
explicit SimpleArray(ToMoveCtor<SimpleArray<T,Algo> > obj);
};
SimpleArray allocates space dynamically.
SimpleArray is std movable. The original object is nullified during the move.
Unlike TempArray, SimpleArray is swappable and movable.
DynArray is the "main" of Arrays. It stores elements in a memory space with some reserved memory behind. It can extend the range of elements, if there is an extra memory. If not and the type T supports moving, then the array extension can be performed with either the memory block extension (using MemExtend()) or the total reallocation with the content moving. You can also release the extra memory. All details of the array functionality can be customized using the second template argument, which is an Algorithm Package of array algorithms.
Alone with standard methods, DynArray has a bunch of methods to initialize and control the array content.
template <class T,class Algo=ArrayAlgo<T> >
class DynArray : NoCopy
{
....
public:
// constructors
DynArray() noexcept ;
template <class S>
explicit DynArray(std::initializer_list<S> il);
explicit DynArray(ulen len);
DynArray(ulen len,ulen maxlen);
DynArray(DoReserveType,ulen maxlen);
explicit DynArray(DoRaw dotype);
template <class ... SS>
explicit DynArray(DoFill dotype,SS && ... ss);
DynArray(DoCopy dotype,const T src[]);
template <class S>
DynArray(DoCast dotype,const S src[]);
DynArray(DoSwap dotype,T objs[]);
template <class Creator>
DynArray(DoCreate dotype,Creator creator);
template <class Builder>
DynArray(DoBuildType,Builder builder);
~DynArray();
// std move
DynArray(DynArray<T,Algo> &&obj) noexcept;
DynArray<T,Algo> & operator = (DynArray<T,Algo> &&obj) noexcept;
Non-default constructors reserve some space and build some elements. The DoRaw, ..., DoCreate arguments carry the length and the reserved length of the array to be used.
Non-default constructors work the same way as the correspondent extend...() methods.
DynArray(std::initializer_list<S> il) uses the initialization list to "cast" elements from the given.
DynArray(ulen len) creates the array of the len elements using the default constructor.
DynArray(ulen len,ulen maxlen) also performs the default initialization of the len elements, but reserves a space for the maxlen elements. An exception is thrown, if len is greater than maxlen.
DynArray(DoReserveType,ulen maxlen) just reserves a space.
DynArray(DoRaw dotype) performs the "raw" initialization. If the T is a POD type, elements are not initialized. If not, the default initialization is performed.
DynArray(DoFill dotype,SS && ... ss) creates new elements using some constructor, the specified arguments are forwarded to this constructor.
DynArray(DoCopy dotype,const T src[]) creates new elements using the copy constructor, src is a range of length dotype.getLen(), its members are copied memberwise.
DynArray(DoCast dotype,const S src[]) creates new elements using the "cast" constructor, src is a range of length dotype.getLen(), its members are casted memberwise.
DynArray(DoSwap dotype,T objs[]) creates new elements using the "swap move" approach. I.e. new elements are created using the default constructor and swapped with members of the range objs.
DynArray(DoCreate dotype,Creator creator) uses the given Creator object to create new elements.
DynArray(DoBuildType,Builder builder) uses the given Builder object to create new elements.
DynArray is std movable. The original object is nullified during the move.
// range access T * getPtr(); const T * getPtr() const; const T * getPtr_const() const; ulen getLen() const; ulen getMaxLen() const; ulen getExtraLen() const; // index access T & operator [] (ulen index); const T & operator [] (ulen index) const; T & at(ulen index); const T & at(ulen index) const;
Range access and index access methods are standard. Two additional methods report memory allocation.
getMaxLen() returns the reserved array length (in objects).
getExtraLen() is the extra space available (in objects).
// methods void cloneTo(DynArray<T,Algo> &ret) const; void reserve(ulen extra_len); void erase();
cloneTo() clones the array. The reserved length is also preserved. The type T must be copyable.
reserve() reserves the space for extra_len objects.
erase() erases the array, it gets to the null state, with no objects and no memory allocated.
// shrink ulen shrink(ulen delta_len); bool shrink_one(); ulen shrink_all(); void shrink_extra(); void shrink_reserve(ulen maxlen); void shrink_reserve();
shrink() destroys the last up to delta_len elements of the array. It returns the number of elements destroyed.
shrink_one() destroys the last element (if any). It returns true, if the element is destroyed, and false if the array is empty.
shrink_all() destroys all array elements. It returns the number of elements destroyed.
These three methods doesn't change the reserved array length.
shrink_extra() shrinks the memory block, returning the extra memory to the heap. After this method the reserved array length becomes equal to the array length.
shrink_reserve(ulen maxlen) may shrink the memory block to store the given number of elements. It gives a more precise control over the reserved memory. It works only if the following natural condition satisfied: maxlen<getMaxLen() and maxlen>=getLen().
shrink_reserve() works as shrink_reserve(ulen maxlen) with maxlen equals "double size" of the actual array length.
// extend PtrLen<T> extend_raw(ulen delta_len); PtrLen<T> extend_default(ulen delta_len); template <class ... SS> PtrLen<T> extend_fill(ulen delta_len,SS && ... ss); PtrLen<T> extend_copy(ulen delta_len,const T src[]); PtrLen<T> extend_copy(PtrLen<const T> src); template <class S> PtrLen<T> extend_cast(ulen delta_len,const S src[]); template <class S> PtrLen<T> extend_cast(PtrLen<const S> src); PtrLen<T> extend_swap(ulen delta_len,T objs[]); PtrLen<T> extend_swap(PtrLen<T> objs); template <class Creator> PtrLen<T> extend(ulen delta_len,Creator creator); template <class Builder> PtrLen<T> extend(Builder builder);
extend...() is a family of methods, which extends the array with number elements. They differ in element construction ways. All of them, except the last, has the first argument delta_len, which is the number of new elements. The return value is the range of these new elements. extend...() are transactional, i.e. if the method has failed, then the array remains in the original state, an exception is thrown.
extend_raw() for POD types leaves new elements uninitialized. For other types it works like extend_default().
extend_default() creates new elements using the default constructor.
extend_fill() creates new elements using some constructor, the specified arguments are forwarded to this constructor.
extend_copy() creates new elements using the copy constructor, src is a range of length delta_len, its members are copied memberwise.
extend_cast() creates new elements using the "cast" constructor, src is a range of length delta_len, its members are casted memberwise.
extend_swap() creates new elements using the "swap move" approach. I.e. new elements are created using the default constructor and swapped with members of the range objs.
Two final extend() are generic. The first of them uses the given Creator object to create new elements, and the second uses the Builder. See below explanations about Creators and Builders.
// append T * append_raw(); T * append_default(); template <class ... SS> T * append_fill(SS && ... ss); T * append_copy(const T &src); T * append_swap(T &obj); template <class Creator> T * append(Creator creator);
append...() is a family of methods to append one element to the array. They are similar to the correspondent extend...() methods and do exactly the same with delta_len equals 1. The return value is a pointer to the created element.
// apply template <class FuncInit> void apply(FuncInit func_init); template <class FuncInit> void apply(FuncInit func_init) const; template <class FuncInit> void apply_const(FuncInit func_init) const; template <class FuncInit> void applyReverse(FuncInit func_init); template <class FuncInit> void applyReverse(FuncInit func_init) const; template <class FuncInit> void applyReverse_const(FuncInit func_init) const;
apply...() methods are standard.
// swap/move objects void objSwap(DynArray<T,Algo> &obj); explicit DynArray(ToMoveCtor<DynArray<T,Algo> > obj); };
DynArray is swappable and movable.
RefArray and AtomicRefArray are copyable arrays with efficient copy constructors. It's assumed, the type T is copyable. Multiple copies of arrays may share the same state. The state has a reference counter, which is used to end its life-time. The only difference between RefArray and AtomicRefArray is the former uses the atomic reference counting to be viable in a multi-threaded environment.
RefArray has a simulated value semantic. It means, that you may consider different copies of an array as independent variables, despite they may share the state. That is because RefArray provides only a constant access to elements. So far you don't violate this constantness (using the const-cast, for example), you may safely assume that each copy is different. To modify the array you have to call the method modify() (or modifyReverse()). This method "unshares" the array, if necessary, and gives a non-constant access to elements. modifyReverse() is doing the same thing as modify(), but gives a "reversed" range of elements.
template <class T,class Algo=ArrayAlgo<T> >
class RefArray
{
....
public:
// constructors
RefArray() noexcept;
template <class S>
explicit RefArray(std::initializer_list<S> il);
explicit RefArray(ulen len);
RefArray(ulen len,ulen maxlen);
RefArray(DoReserveType,ulen maxlen);
explicit RefArray(DoRaw dotype);
template <class ... SS>
explicit RefArray(DoFill dotype,SS && ... ss);
RefArray(DoCopy dotype,const T src[]);
template <class S>
RefArray(DoCast dotype,const S src[]);
RefArray(DoSwap dotype,T objs[]);
template <class Creator>
RefArray(DoCreate dotype,Creator creator);
template <class Builder>
RefArray(DoBuildType,Builder builder);
~RefArray();
// range access
const T * getPtr() const;
const T * getPtr_const() const;
ulen getLen() const;
ulen getMaxLen() const;
ulen getExtraLen() const;
// index access
const T & operator [] (ulen index) const;
const T & at(ulen index) const;
// methods
PtrLen<T> modify();
PtrLenReverse<T> modifyReverse() { return RangeReverse(modify()); }
void cloneTo(RefArray<T,Algo> &ret) const;
void reserve(ulen extra_len);
void erase();
// shrink
ulen shrink(ulen delta_len);
bool shrink_one();
ulen shrink_all();
void shrink_extra();
void shrink_reserve(ulen maxlen);
void shrink_reserve();
// extend
PtrLen<T> extend_raw(ulen delta_len);
PtrLen<T> extend_default(ulen delta_len);
template <class ... SS>
PtrLen<T> extend_fill(ulen delta_len,SS && ... ss);
PtrLen<T> extend_copy(ulen delta_len,const T src[]);
PtrLen<T> extend_copy(PtrLen<const T> src);
template <class S>
PtrLen<T> extend_cast(ulen delta_len,const S src[]);
template <class S>
PtrLen<T> extend_cast(PtrLen<const S> src);
PtrLen<T> extend_swap(ulen delta_len,T objs[]);
PtrLen<T> extend_swap(PtrLen<T> objs);
template <class Creator>
PtrLen<T> extend(ulen delta_len,Creator creator);
template <class Builder>
PtrLen<T> extend(Builder builder);
// append
T * append_raw();
T * append_default();
template <class ... SS>
T * append_fill(SS && ... ss);
T * append_copy(const T &src);
T * append_swap(T &obj);
template <class Creator>
T * append(Creator creator);
// apply
template <class FuncInit>
void apply_modify(FuncInit func_init);
template <class FuncInit>
void apply(FuncInit func_init) const;
template <class FuncInit>
void applyReverse_modify(FuncInit func_init);
template <class FuncInit>
void applyReverse(FuncInit func_init) const;
// swap/move objects
void objSwap(RefArray<T,Algo> &obj);
explicit RefArray(ToMoveCtor<RefArray<T,Algo> > obj);
};
The RefArray methods are mostly the same as DynArray methods. The differences are: index and range access methods gives only the constant access, and new class-specific methods modify() and modifyReverse() exist.
apply_modify() applies the functor, given by a Functor Init, to array elements. The array is unshared first.
applyReverse_modify() applies the functor, given by a Functor Init, to array elements in the reverse order. The array is unshared first.
apply() applies the functor, given by a Functor Init, to array elements. This is a constant method.
applyReverse() applies the functor, given by a Functor Init, to array elements in the reverse order. This is a constant method.
AtomicRefArray methods shrink_extra() and shrink_reserve() are not working, if the array is shared.
Collector is not an array! The purpose of this container is to be an efficient collector of elements. This container stores a sequence of elements in a list of arrays. So appending and extending operations are the most efficient. At desired moment you can copy or move this sequence into true array. Or you can "flat" the Collector itself.
template <class T,class Algo=ArrayAlgo<T> >
class Collector : NoCopy
{
....
public:
// constructors
static const ulen DefaultBlockLen = 1024 ;
static const ulen MinBlockLen = 10 ;
explicit Collector(ulen block_len=DefaultBlockLen) noexcept;
~Collector();
// std move
Collector(Collector<T,Algo> &&obj) noexcept;
Collector<T,Algo> & operator = (Collector<T,Algo> &&obj) noexcept;
The argument of the Collector constructor is the number of elements in the single element block. The default value is 1024.
Collector is std movable. The original object is nullified during the move.
// methods ulen getLen() const; void erase(); template <class Container> void extractTo(Container &ret) { Container temp(DoBuild,Extractor(this)); Swap(temp,ret); } template <class Container> void copyTo(Container &ret) const { Container temp(DoBuild,Copyrator(this)); Swap(temp,ret); } PtrLen<T> flat();
Collector does not provide a direct access to its content.
getLen() is the number of the collected elements.
erase() cleanup the container and put it into the null state.
extractTo() moves the content to another (array) container. The target container must have the Build constructor. Collector becomes empty after this operation.
copyTo() copies the content to another (array) container. The target container must have the Build constructor.
flat() rebuilds the internal data storage, making it continuous. It returns the range of elements. This range remains valid after extend or append operations (but may become invalid after any other modifying operations).
// shrink ulen shrink(ulen delta_len); bool shrink_one(); ulen shrink_all(); // extend PtrLen<T> extend_raw(ulen delta_len); PtrLen<T> extend_default(ulen delta_len); template <class ... SS> PtrLen<T> extend_fill(ulen delta_len,SS && ... ss); PtrLen<T> extend_copy(ulen delta_len,const T src[]); PtrLen<T> extend_copy(PtrLen<const T> src); template <class S> PtrLen<T> extend_cast(ulen delta_len,const S src[]); template <class S> PtrLen<T> extend_cast(PtrLen<const S> src); PtrLen<T> extend_swap(ulen delta_len,T objs[]); PtrLen<T> extend_swap(PtrLen<T> objs); template <class Creator> PtrLen<T> extend(ulen delta_len,Creator creator); template <class Builder> PtrLen<T> extend(Builder builder); // append T * append_raw(); T * append_default(); template <class ... SS> T * append_fill(SS && ... ss); T * append_copy(const T &src); T * append_swap(T &obj); template <class Creator> T * append(Creator creator);
shrink...(), extend...() and append...() methods are the same as for DynArray.
// swap/move object void objSwap(Collector<T,Algo> &obj); explicit Collector(ToMoveCtor<Collector<T,Algo> > obj); };
Collector is swappable and movable.
CCore Array implementation is based on the Array Algorithm Packages. The default package is the ArrayAlgo.
template <class T,class Flags=GetNoThrowFlags<T> > struct ArrayAlgo;
The exact implementation depends on the kind of T.
If the type T defines the inner type ArrayAlgoType, then this type will be used as the package.
Otherwise there are two variants: one for the POD types, another for the other (class) types.
ArrayAlgo_class is the generic (class) variant. It is assumed, that the following operations: ~T(), Move(T *,Place<void>) and Swap(T &,T &) are no-throw. The template argument Flags provides two flags: Default_no_throw and Copy_no_throw. The flag is true, if the correspondent constructor (default or copy) is no-throw or does not exist. It's an obligation of the class.
template <class T,class Flags>
struct ArrayAlgo_class : ArrayAlgoMemBase
{
//
// assume ~T() , Move(T *,Place<void>) , Swap(T &,T &) no-throw
//
//
// Create...() : clean on throw
//
enum PropFlagType
{
Default_no_throw = Flags::Default_no_throw,
Copy_no_throw = Flags::Copy_no_throw,
MoveTo_exist = true
};
static PtrLen<T> Create_raw(Place<void> place,ulen len);
static PtrLen<T> Create_default(Place<void> place,ulen len);
template <class ... SS>
static PtrLen<T> Create_fill(Place<void> place,ulen len,SS && ... ss);
static PtrLen<T> Create_copy(Place<void> place,ulen len,const T src[]);
template <class S>
static PtrLen<T> Create_cast(Place<void> place,ulen len,const S src[]);
static PtrLen<T> Create_swap(Place<void> place,ulen len,T objs[]);
//
// using Creator
//
template <class Creator>
static PtrLen<T> Create(Place<void> place,ulen len,Creator creator);
//
// Single
//
static T * Create_swap(Place<void> place,T &obj) noexcept( Default_no_throw );
//
// ProvideLen() : double extension
//
static ulen ProvideLen(ulen len,ulen maxlen,ulen extra_len);
//
// MoveTo() : no-throw
//
static PtrLen<T> MoveTo(T *ptr,ulen len,Place<void> place) noexcept;
//
// single Destroy() : no-throw
//
static void Destroy(T *ptr) noexcept;
//
// Destroy() : no-throw
//
static void Destroy(T *ptr,ulen len) noexcept;
//
// Guards
//
class BuildGuard;
class CreateGuard;
class CreateGuard_nothrow;
template <bool no_throw> class CreateGuardNoThrow;
};
The package has the following members.
Flags Default_no_throw and Copy_no_throw are copied from the Flags.
Flag MoveTo_exist is always true.
Create_...() creates a range of objects of the type T at the given place. To create an object, the correspondent constructor is called. Objects are created in the ascending order. If some constructor throw an exception, then the previously constructed objects are destroyed and the exception is propagated. The range of the created objects is returned. The first two arguments are: the place to construct and the number of elements to construct. The place must be properly aligned and the memory space must be large enough to contain the required number of objects.
Create_raw() and Create_default() use the default constructor.
Create_fill() uses a constructor with the given set of arguments for each object.
Create_copy() uses the copy constructor. The arguments are memberwise selected from the provided range of objects.
Create_cast() uses the "cast" constructor. The arguments are memberwise selected from the provided range of objects.
Create_swap() uses the default constructor and the swap operation to create objects. The arguments for the swap are memberwise selected from the provided range of objects.
Create() uses the given creator for the object creation. See below about creators.
The single Create_swap() function "creates-swap" a single object.
ProvideLen() is used to reallocate an array. It is called if there is no sufficient memory to extend the array. The len is the array length, the extra_len is the number of extra objects to be constructed, the maxlen is the allocated length. The length returned is sufficient to extend the array and at least double of the maxlen. An exception is thrown in case of overflow.
MoveTo() moves the given range to a new place. The old place becomes a raw memory. The range of new objects is returned.
Single Destroy() destroys the object (but don't free the memory).
Range Destroy() destroys the given range of objects.
There are four inner "guard" classes. They are used to created a range of objects. Guards are responsible for the object destruction in case of failure.
class BuildGuard : NoCopy
{
Place<void> place;
T *ptr;
ulen len;
public:
explicit BuildGuard(Place<void> place_) : place(place_),ptr(place_),len(0) {}
~BuildGuard() { if( len ) Destroy(ptr,len); }
Place<void> at() const { return place; }
void operator ++ () { place+=sizeof (T); len++; }
PtrLen<T> disarm() { return Range(ptr,Replace_null(len)); }
};
BuildGuard is used to create a range of objects. The constructor argument is the place for the range. It must be properly aligned and the memory space must be large enough to store the range.
at() is the current place for the object creation.
operator ++ () must be called after the successful creation of an object at the current position, it moves to the next object position.
disarm() is called after the desired number of objects are created. It "disarms" the guard and returns the created range.
The BuildGuard destructor destroys the created objects. It must be disarmed to prevent the destruction.
class CreateGuard : public BuildGuard
{
public:
CreateGuard(Place<void> place,ulen /*final_len*/) : BuildGuard(place) {}
~CreateGuard() {}
};
CreateGuard is used for creation a range of objects with the given length. Constructor's arguments are: the place of the range and the final number of objects. The remaining methods are the same as for BuildGuard. Using CreateGuard you must create the exact number of objects.
class CreateGuard_nothrow : NoCopy
{
Place<void> place;
T *ptr;
ulen final_len;
public:
CreateGuard_nothrow(Place<void> place_,ulen final_len_) : place(place_),ptr(place_),final_len(final_len_) {}
Place<void> at() const { return place; }
void operator ++ () { place+=sizeof (T); }
PtrLen<T> disarm() { return Range(ptr,final_len); }
};
CreateGuard_nothrow is a "no-throw" variant of the CreateGuard. It is used for the creation of a range of objects with the given length, if there will be no exceptions during the objects construction.
template <bool no_throw>
class CreateGuardNoThrow : NoCopy
{
public:
CreateGuardNoThrow(Place<void> place,ulen final_len);
~CreateGuardNoThrow();
};
CreateGuardNoThrow is the one of the CreateGuard and CreateGuard_nothrow, depending on the no_throw template parameter.
Below is the implementation of the generic Create, it uses the CreateGuardNoThrow.
template <class Creator>
static PtrLen<T> Create(Place<void> place,ulen len,Creator creator) noexcept( Creator::NoThrow )
{
CreateGuardNoThrow<Creator::NoThrow> guard(place,len);
FunctorTypeOf<Creator> func(creator);
for(; len ;len--,++guard) func(guard.at());
return guard.disarm();
}
ArrayAlgo_pod is the POD variant.
template <class T>
struct ArrayAlgo_pod : ArrayAlgoMemBase
{
//
// Create...() : clean on throw
//
enum PropFlagType
{
Default_no_throw = true,
Copy_no_throw = true,
MoveTo_exist = true
};
static PtrLen<T> Create_raw(Place<void> place,ulen len);
static PtrLen<T> Create_default(Place<void> place,ulen len);
template <class ... SS>
static PtrLen<T> Create_fill(Place<void> place,ulen len,SS && ... ss);
static PtrLen<T> Create_copy(Place<void> place,ulen len,const T src[]);
template <class S>
static PtrLen<T> Create_cast(Place<void> place,ulen len,const S src[]);
static PtrLen<T> Create_swap(Place<void> place,ulen len,T objs[]);
//
// using Creator
//
template <class Creator>
static PtrLen<T> Create(Place<void> place,ulen len,Creator creator) noexcept( Creator::NoThrow );
//
// Single
//
static T * Create_swap(Place<void> place,T &obj) noexcept;
//
// ProvideLen() : double extension
//
static ulen ProvideLen(ulen len,ulen maxlen,ulen extra_len);
//
// MoveTo() : no-throw
//
static PtrLen<T> MoveTo(T *ptr,ulen len,Place<void> place) noexcept;
//
// single Destroy() : empty
//
static void Destroy(T *ptr) {}
//
// Destroy() : empty
//
static void Destroy(T *ptr,ulen len) {}
//
// Guards
//
class BuildGuard;
class CreateGuard;
class CreateGuard_nothrow;
template <bool no_throw> class CreateGuardNoThrow;
};
Flags Default_no_throw, Copy_no_throw and MoveTo_exist are all true.
Destroy() functions are empty.
Create_raw() is not using default initialization. It leaves the memory uninitialized.
ArrayAlgo_mini is the mini variant without object moving. It is assumed only, that the destructor ~T() is no-throw. Swap and move are not used.
template <class T,class Flags>
struct ArrayAlgo_mini : ArrayAlgoMemBase
{
//
// assume ~T() no-throw
//
//
// Create...() : clean on throw
//
enum PropFlagType
{
Default_no_throw = Flags::Default_no_throw,
Copy_no_throw = Flags::Copy_no_throw,
MoveTo_exist = false
};
static PtrLen<T> Create_raw(Place<void> place,ulen len);
static PtrLen<T> Create_default(Place<void> place,ulen len);
template <class ... SS>
static PtrLen<T> Create_fill(Place<void> place,ulen len,SS && ... ss);
static PtrLen<T> Create_copy(Place<void> place,ulen len,const T src[]);
template <class S>
static PtrLen<T> Create_cast(Place<void> place,ulen len,const S src[]);
//
// using Creator
//
template <class Creator>
static PtrLen<T> Create(Place<void> place,ulen len,Creator creator) noexcept( Creator::NoThrow );
//
// single Destroy() : no-throw
//
static void Destroy(T *ptr) noexcept;
//
// Destroy() : no-throw
//
static void Destroy(T *ptr,ulen len) noexcept;
//
// Guards
//
class BuildGuard;
class CreateGuard;
class CreateGuard_nothrow;
template <bool no_throw> class CreateGuardNoThrow;
};
The flag MoveTo_exist is false.
The following functions: Create_swap(), ProvideLen(), MoveTo() are omitted. Array, constructed with this algorithm package, cannot be extended beyond the initial allocation length.
Creator is a functor, creating objects. It looks like this:
class Creator // copy efficient { .... public: enum NoThrowFlagType { NoThrow = .... }; Creator(....); T * operator () (Place<void> place) noexcept(NoThrow); };
Or like this:
class Creator // copy efficient functor initializer { .... public: enum NoThrowFlagType { NoThrow = .... }; Creator(....); class FunctorType : NoCopy // heavy functor { .... public: explicit FunctorType(Creator init); ~FunctorType(); T * operator () (Place<void> place) noexcept(NoThrow); }; };
You may use a Creator in a generic array "Creator" constructors and methods.
Builder is a functor, creating a range of objects. It looks like:
class Builder // copy efficient { public: Builder(....); ulen getLen() const; PtrLen<T> operator () (Place<void> place) const; // may create up to getLen() objects };
Or like this:
class Builder // copy efficient functor initializer { public: Builder(....); ulen getLen() const; class FunctorType : NoCopy // heavy functor { .... public: explicit FunctorType(Builder init); ~FunctorType(); PtrLen<T> operator () (Place<void> place) const; // may create up to getLen() objects }; };
Builder reports the maximum object number it will create through the method getLen(). operator () gets the place for the range of getLen() elements, it either creates the range of objects up to this length and returns it, or leaves the memory raw and throws an exception.
You may use a Builder in a generic array "Builder" constructors and methods.
There is a list of standard creators.
/* struct Creator_default<T,bool no_throw> */ template <class T,bool no_throw> struct Creator_default { enum NoThrowFlagType { NoThrow = no_throw }; Creator_default() {} T * operator () (Place<void> place) noexcept( no_throw ) { return new(place) T(); } }; /* struct Creator_fill<T,SS> */ template <class T,class ... SS> struct Creator_fill { enum NoThrowFlagType { NoThrow = false }; ForwardTuple<SS...> ss; explicit Creator_fill(SS && ... ss_) : ss( std::forward<SS>(ss_)... ) {} T * operator () (Place<void> place) { T *ret; ss.call( [&ret,place] (SS && ... ss) { ret=new(place) T( std::forward<SS>(ss)... ); } ); return ret; } }; /* struct Creator_copy<T,bool no_throw> */ template <class T,bool no_throw> struct Creator_copy { enum NoThrowFlagType { NoThrow = no_throw }; const T *src; explicit Creator_copy(const T *src_) : src(src_) {} T * operator () (Place<void> place) noexcept( no_throw ) { return new(place) T(*(src++)); } }; /* struct Creator_cast<T,S> */ template <class T,class S> struct Creator_cast { enum NoThrowFlagType { NoThrow = false }; const S *src; explicit Creator_cast(const S *src_) : src(src_) {} T * operator () (Place<void> place) { return new(place) T(*(src++)); } }; /* struct Creator_swap<T,Algo> */ template <class T,class Algo> struct Creator_swap { enum NoThrowFlagType { NoThrow = Algo::Default_no_throw }; T *objs; explicit Creator_swap(T *objs_) : objs(objs_) {} T * operator () (Place<void> place) noexcept( Algo::Default_no_throw ) { return Algo::Create_swap(place,*(objs++)); } };
All default algorithm packages inherit the base class ArrayAlgoMemBase.
struct ArrayAlgoMemBase
{
static void * MemAlloc(ulen len) { return ::CCore::MemAlloc(len); }
static bool MemExtend(void *mem,ulen len) { return ::CCore::MemExtend(mem,len); }
static bool MemShrink(void *mem,ulen len) { return ::CCore::MemShrink(mem,len); }
static void MemFree(void *mem) { ::CCore::MemFree(mem); }
};
This class defines default memory-management functions, used by arrays to allocate, extend, shrink and release memory blocks. If you define a custom algorithm package for your purpose, you may define custom memory allocation functions with the following semantic.
MemAlloc() allocates a memory block of the required length. The block is aligned. An exception is thrown in case of error.
MemFree() releases the previously allocated memory block. The function is never called with the null argument.
MemExtend() tries to extend the previously allocated memory block length to be at least the given argument. If the return value is true, then the attempt was successful.
MemShrink() shrinks the memory block length to be at least the given argument. The return value is never used.