While refactoring, I wanted to change an array where entries are added to an std::vector, but for compatibility (persistency, downgrading,...), it still needs to have an upper limit.
What is the best way (elegant, stl-like, limited extra code) to have an stl-like container which is limited in size, so you know that inserting an entry fails?
Edit:
To clarify: I would like an stl-like container, that starts empty, that you can fill with entries and possibly remove entries and that iterate over the filled-in entries, but that doesn't allow to put in more than e.g. 50 entries, so almost like a sequential contrainer, but with an upper-limit.
A simple solution would be encapsulating a vector inside your own limited size container. You could use private composition or private inheritance --note that private inheritance models implemented in terms of and does not have some of the shortcomings of public inheritance.
EDIT: Sketch of the solution with private inheritance
template <typename T, unsigned int N>
class fixed_vector : std::vector<T>
{
typedef std::vector<T> vector_type;
public:
typedef typename vector_type::reference reference;
typedef typename vector_type::const_reference const_reference;
typedef typename vector_type::iterator iterator;
typedef typename vector_type::const_iterator const_iterator;
typedef typename vector_type::value_type value_type;
typedef typename vector_type::size_type size_type;
fixed_vector() : vector_type() {}
fixed_vector( size_type size, value_type const & value = value_type() )
: vector_type(size,value)
{}
void push_back( value_type v ) {
ensure_can_grow();
vector_type::push_back( v );
}
iterator insert( iterator position, value_type const & v ) {
ensure_can_grow();
vector_type::insert( position, v );
}
void reserve( size_type size ) {
if ( size > N ) throw std::invalid_argument();
vector_type::reserve( size );
}
size_type capacity() const {
// In case the default implementation acquires by default
// more than N elements, or the vector grows to a higher capacity
return std::min( vector_type::capacity(), N );
}
// provide other insert methods if required, with the same pattern
using vector_type::begin;
using vector_type::end;
using vector_type::operator[];
using vector_type::erase;
using vector_type::size;
using vector_type::empty;
private:
void ensure_can_grow() const {
// probably a different exception would make sense here:
if ( this->size() == N ) throw std::bad_alloc();
}
};
There is quite a bit of hand-waving there... std::vector take more arguments that could be added to the façade. If you need any of the other methods or typedefs, you can just bring them into scope with a using declaration, redefine the typedef, or implement the adaptor with your particular test.
Also, in this implementation the size is a compile time constant, but it would be really simple to modify it into a constructor parameter.
Customize the vector class to impose an upper limit.
Probably, you can have a new api exposed which will check the size against the upper limit and return false if exceeds otherwise call the regular insertion method.
Have a look at this static_vector implementation which I found a while ago. I think it does exactly what you want.
It's distributed under the very liberal boost license, so you're allowed to do just about anything with it.
You can create a custom allocator (e.g. derived from std::allocator) that refuses to allocate an array larger than a given size.
Note that you need to call reserve( vector_max ) on the resulting object before adding things to it. I'm filing a defect against the C++ standard, as the requirement should be unnecessary (and it is, on recent versions of GCC).
template< typename T, size_t N >
struct limited_alloc : std::allocator< T > {
size_t max_size() const { return N; }
typename std::allocator<T>::pointer allocate( size_t n ) {
if ( n < N ) return std::allocator<T>::allocate( n );
throw std::length_error( "array too large" );
}
limited_alloc() {} // silly cruft for standard requirements:
template< typename T2 >
limited_alloc( limited_alloc<T2,N> const & ) {}
template< typename T2 >
struct rebind { typedef limited_alloc<T2,N> other; };
};
enum { vector_max = 40 };
template< typename T >
struct limited_vector {
typedef std::vector< T, limited_alloc< T, vector_max > > type;
};
void f() {
limited_vector< int >::type x;
x.reserve( vector_max );
x.assign( vector_max + 1, 3 ); // throws.
}
Take a look at Boost.Array
As replacement for ordinary arrays, the STL provides class std::vector. However, std::vector<> provides the semantics of dynamic arrays. Thus, it manages data to be able to change the number of elements. This results in some overhead in case only arrays with static size are needed.
Take a look at boost::array
Edit: for add/delete boost::optional can be used as a element type of boost::array.
Related
I registered a custom linestring type, it fulfills the random access range concept. The tag is registered too.
So i tried using boost::geometry::simplify on it.
Then i found out boost::geometry::traits::clear,resize,push_back<MyLineStringType> needs to implemented if the type doesn't have these operations with same names.
Afaik not documented as requirements, but okay, was able to implement it for my type.
Then the ugly thing:
simplify doesn't use the traits::push_back internally but std::back_inserter.
So it's basicly impossible to use a custom type without a member push_back.
Or am i allowed to spezialize the back_inserter inside the stl namespace?
Is there any reason why simplify uses the back_inserter instead of the traits::push_back which seems to meant for this case? To me this looks like bad design.
Edit: I cannot change the interface of my linestring.
Edit2: code fragments from simplify using std::back_inserter instead of boost::geometry::traits<>::push_back
struct simplify_copy
{
template <typename Range, typename Strategy, typename Distance>
static inline void apply(Range const& range, Range& out,
Distance const& , Strategy const& )
{
std::copy
(
boost::begin(range), boost::end(range), std::back_inserter(out)
);
}
};
template<std::size_t Minimum>
struct simplify_range
{
template <typename Range, typename Strategy, typename Distance>
static inline void apply(Range const& range, Range& out,
Distance const& max_distance, Strategy const& strategy)
{
/* ... */
if (boost::size(range) <= int(Minimum) || max_distance < 0.0)
{
/* ... */
}
else
{
simplify_range_insert::apply
(
range, std::back_inserter(out), max_distance, strategy
);
}
}
};
Suppose I have a bunch of vectors:
vector<int> v1;
vector<double> v2;
vector<int> v3;
all of the same length. Now, for every index i, I would like to be able to treat (v1[i], v2[i], v3[i]) as a tuple, and maybe pass it around. In fact, I want to have a a vector-of-tuples rather than a tuple-of-vectors, using which I can do the above. (In C terms, I might say an array-of-structs rather than a struct-of-arrays). I do not want to effect any data reordering (think: really long vectors), i.e. the new vector is backed by the individual vectors I pass in. Let's .
Now, I want the class I write (call it ToVBackedVoT for lack of a better name) to support any arbitrary choice of vectors to back it (not just 3, not int, double and int, not every just scalars). I want the vector-of-tuples to be mutable, and for no copies to be made on construction/assignments.
If I understand correctly, variadic templates and the new std::tuple type in C++11 are the means for doing this (assuming I don't want untyped void* arrays and such). However, I only barely know them and have never worked with them. Can you help me sketch out how such a class will look like? Or how, given
template <typename ... Ts>
I can express something like "the list of template arguments being the replacement of each typename in the original template arguments with a vector of elements of this type"?
Note: I think I might also want to later be able to adjoin additional vectors to the backing vectors, making an instance of ToVBackedVoT<int, double, int> into, say, an instance of ToVBackedVoT<int, double, int, unsigned int>. So, bear that in mind when answering. This is not critically important though.
One idea is to keep the storage in the "struct of array" style in form of vectors for good performance if only a subset of the fields are used for a particular task. Then, for each kind of task requiring a different set of fields, you can write a lightweight wrapper around some of those vectors, giving you a nice random access iterator interface similar to what std::vector supports.
Concerning the syntax of variadic templates, this is how a wrapper class (without any iterators yet) could look like:
template<class ...Ts> // Element types
class WrapMultiVector
{
// references to vectors in a TUPLE
std::tuple<std::vector<Ts>&...> m_vectors;
public:
// references to vectors in multiple arguments
WrapMultiVector(std::vector<Ts> & ...vectors)
: m_vectors(vectors...) // construct tuple from multiple args.
{}
};
To construct such a templated class, it's often preferred to have a template type deducting helper function available (similar to those make_{pair|tuple|...} functions in std):
template<class ...Ts> // Element types
WrapMultiVector<Ts...> makeWrapper(std::vector<Ts> & ...vectors) {
return WrapMultiVector<Ts...>(vectors...);
}
You already see different types of "unpacking" the type list.
Adding iterators suitable to your application (you requested in particular random access iterators) is not so easy. A start could be forward only iterators, which you might extend to random access iterators.
The following iterator class is capable of being constructed using a tuple of element iterators, being incremented and being dereferenced to obtain a tuple of element references (important for read-write access).
class iterator {
std::tuple<typename std::vector<Ts>::iterator...> m_elemIterators;
public:
iterator(std::tuple<typename std::vector<Ts>::iterator...> elemIterators)
: m_elemIterators(elemIterators)
{}
bool operator==(const iterator &o) const {
return std::get<0>(m_elemIterators) == std::get<0>(o.m_elemIterators);
}
bool operator!=(const iterator &o) const {
return std::get<0>(m_elemIterators) != std::get<0>(o.m_elemIterators);
}
iterator& operator ++() {
tupleIncrement(m_elemIterators);
return *this;
}
iterator operator ++(int) {
iterator old = *this;
tupleIncrement(m_elemIterators);
return old;
}
std::tuple<Ts&...> operator*() {
return getElements(IndexList());
}
private:
template<size_t ...Is>
std::tuple<Ts&...> getElements(index_list<Is...>) {
return std::tie(*std::get<Is>(m_elemIterators)...);
}
};
For demonstration purposes, two different patterns are in this code which "iterate" over a tuple in order to apply some operation or construct a new tuple with some epxression to be called per element. I used both in order to demonstrate alternatives; you can also use the second method only.
tupleIncrement: You can use a helper function which uses meta programming to index a single entry and advance the index by one, then calling a recursive function, until the index is at the end of the tuple (then there is a special case implementation which is triggered using SFINAE). The function is defined outside of the class and not above; here is its code:
template<std::size_t I = 0, typename ...Ts>
inline typename std::enable_if<I == sizeof...(Ts), void>::type
tupleIncrement(std::tuple<Ts...> &tup)
{ }
template<std::size_t I = 0, typename ...Ts>
inline typename std::enable_if<I < sizeof...(Ts), void>::type
tupleIncrement(std::tuple<Ts...> &tup)
{
++std::get<I>(tup);
tupleIncrement<I + 1, Ts...>(tup);
}
This method can't be used to assign a tuple of references in the case of operator* because such a tuple has to be initialized with references immediately, which is not possible with this method. So we need something else for operator*:
getElements: This version uses an index list (https://stackoverflow.com/a/15036110/592323) which gets expanded too and then you can use std::get with the index list to expand full expressions. The IndexList when calling the function instantiates an appropriate index list which is only required for template type deduction in order to get those Is.... The type can be defined in the wrapper class:
// list of indices
typedef decltype(index_range<0, sizeof...(Ts)>()) IndexList;
More complete code with a little example can be found here: http://ideone.com/O3CPTq
Open problems are:
If the vectors have different sizes, the code fails. Better would be to check all "end" iterators for equality; if one iterator is "at end", we're also "at end"; but this would require some logic more than operator== and operator!= unless it's ok to "fake" it in; meaning that operator!= could return false as soon as any operator is unequal.
The solution is not const-correct, e.g. there is no const_iterator.
Appending, inserting etc. is not possible. The wrapper class could add some insert or and / or push_back function in order to make it work similar to std::vector. If your goal is that it's syntactically compatible to a vector of tuples, reimplement all those relevant functions from std::vector.
Not enough tests ;)
An alternative to all the variadic template juggling is to use the boost::zip_iterator for this purpose. For example (untested):
std::vector<int> ia;
std::vector<double> d;
std::vector<int> ib;
std::for_each(
boost::make_zip_iterator(
boost::make_tuple(ia.begin(), d.begin(), ib.begin())
),
boost::make_zip_iterator(
boost::make_tuple(ia.end(), d.end(), ib.end())
),
handle_each()
);
Where your handler, looks like:
struct handle_each :
public std::unary_function<const boost::tuple<const int&, const double&, const int&>&, void>
{
void operator()(const boost::tuple<const int&, const double&, const int&>& t) const
{
// Now you have a tuple of the three values across the vector...
}
};
As you can see, it's pretty trivial to expand this to support an arbitrary set of vectors..
From asker's clarification on how this would be used (code that takes a tuple), I'm going to propose this instead.
//give the i'th element of each vector
template<typename... Ts>
inline tuple<Ts&...> ith(size_t i, vector<Ts>&... vs){
return std::tie(vs[i]...);
}
There's a proposal to allow parameter packs to be saved as members of classes (N3728). Using that, here's some untested and untestable code.
template<typename... Types>
class View{
private:
vector<Types>&... inner;
public:
typedef tuple<Types&...> reference;
View(vector<Types>&... t): inner(t...) {}
//return smallest size
size_t size() const{
//not sure if ... works with initializer lists
return min({inner.size()...});
}
reference operator[](size_t i){
return std::tie(inner[i]...);
}
};
And iteration:
public:
iterator begin(){
return iterator(inner.begin()...);
}
iterator end(){
return iterator(inner.end()...);
}
//for .begin() and .end(), so that ranged-based for can be used
class iterator{
vector<Types>::iterator... ps;
iterator(vector<Types>::iterator... its):ps(its){}
friend View;
public:
//pre:
iterator operator++(){
//not sure if this is allowed.
++ps...;
//use this if not:
// template<typename...Types> void dummy(Types... args){} //global
// dummy(++ps...);
return *this;
}
iterator& operator--();
//post:
iterator operator++(int);
iterator operator--(int);
//dereference:
reference operator*()const{
return std::tie(*ps...);
}
//random access:
iterator operator+(size_t i) const;
iterator operator-(size_t i) const;
//need to be able to check end
bool operator==(iterator other) const{
return std::make_tuple(ps...) == std::make_tuple(other.ps...);
}
bool operator!=(iterator other) const{
return std::make_tuple(ps...) != std::make_tuple(other.ps...);
}
};
You may use something like:
#if 1 // Not available in C++11, so write our own
// class used to be able to use std::get<Is>(tuple)...
template<int... Is>
struct index_sequence { };
// generator of index_sequence<Is>
template<int N, int... Is>
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> { };
template<int... Is>
struct make_index_sequence<0, Is...> : index_sequence<Is...> { };
#endif
// The 'converting' class
// Note that it doesn't check that vector size are equal...
template<typename ...Ts>
class ToVBackedVoT
{
public:
explicit ToVBackedVoT(std::vector<Ts>&... vectors) : data(vectors...) {}
std::tuple<const Ts&...> operator [] (unsigned int index) const
{
return at(index, make_index_sequence<sizeof...(Ts)>());
}
std::tuple<Ts&...> operator [] (unsigned int index)
{
return at(index, make_index_sequence<sizeof...(Ts)>());
}
private:
template <int... Is>
std::tuple<const Ts&...> at(unsigned int index, index_sequence<Is...>) const
{
return std::tie(std::get<Is>(data)[index]...);
}
template <int... Is>
std::tuple<Ts&...> at(unsigned int index, index_sequence<Is...>)
{
return std::tie(std::get<Is>(data)[index]...);
}
private:
std::tuple<std::vector<Ts>&...> data;
};
And to iterate, create an 'IndexIterator' like the one in https://stackoverflow.com/a/20272955/2684539
To adjoin additional vectors, you have to create an other ToVBackedVoT as std::tuple_cat does for std::tuple
Conversion to a std::tuple of vectors (vector::iterators):
#include <iostream>
#include <vector>
// identity
// ========
struct identity
{
template <typename T>
struct apply {
typedef T type;
};
};
// concat_operation
// ================
template <typename Operator, typename ...> struct concat_operation;
template <
typename Operator,
typename ...Types,
typename T>
struct concat_operation<Operator, std::tuple<Types...>, T>
{
private:
typedef typename Operator::template apply<T>::type concat_type;
public:
typedef std::tuple<Types..., concat_type> type;
};
template <
typename Operator,
typename ...Types,
typename T,
typename ...U>
struct concat_operation<Operator, std::tuple<Types...>, T, U...>
{
private:
typedef typename Operator::template apply<T>::type concat_type;
public:
typedef typename concat_operation<
Operator,
std::tuple<Types..., concat_type>,
U...>
::type type;
};
template <
typename Operator,
typename T,
typename ...U>
struct concat_operation<Operator, T, U...>
{
private:
typedef typename Operator::template apply<T>::type concat_type;
public:
typedef typename concat_operation<
Operator,
std::tuple<concat_type>,
U...>
::type type;
};
// ToVectors (ToVBackedVoT)
// =========
template <typename ...T>
struct ToVectors
{
private:
struct to_vector {
template <typename V>
struct apply {
typedef typename std::vector<V> type;
};
};
public:
typedef typename concat_operation<to_vector, T...>::type type;
};
// ToIterators
// ===========
template <typename ...T>
struct ToIterators;
template <typename ...T>
struct ToIterators<std::tuple<T...>>
{
private:
struct to_iterator {
template <typename V>
struct apply {
typedef typename V::iterator type;
};
};
public:
typedef typename concat_operation<to_iterator, T...>::type type;
};
int main() {
typedef ToVectors<int, double, float>::type Vectors;
typedef ToVectors<Vectors, int, char, bool>::type MoreVectors;
typedef ToIterators<Vectors>::type Iterators;
// LOG_TYPE(Vectors);
// std::tuple<
// std::vector<int, std::allocator<int> >,
// std::vector<double, std::allocator<double> >,
// std::vector<float, std::allocator<float> > >
// LOG_TYPE(Iterators);
// std::tuple<
// __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >,
// __gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >,
// __gnu_cxx::__normal_iterator<float*, std::vector<float, std::allocator<float> > > >
}
As an alternative similar to boost::zip_iterator I wrote a zip function with a very simple interface:
vector<int> v1;
vector<double> v2;
vector<int> v3;
auto vec_of_tuples = zip(v1, v2, v3);
For example, iterate over these tuples:
for (auto tuple : zip(v1, v2, v3)) {
int x1; double x2; int x3;
std::tie(x1, x2, x3) = tuple;
//...
}
Here, zip() takes any number of ranges of any type. It returns an adaptor which can be seen as a lazily evaluated range over a tuple of elements originating from the wrapped ranges.
The adaptor is part of my Haskell-style functional library "fn" and implemented using variadic templates.
Currently it doesn't support modification of the original ranges' values via the adaptor because of the design of the library (it's intended to be used with non-mutable ranges like in functional programming).
A brief explanation on how this is done is: zip(...) returns an adaptor object which implements begin() and end(), returning an iterator object. The iterator holds a tuple of iterators to the wrapped ranges. Incrementing the iterator increments all wrapped iterators (which is implemented using an index list and unpacking an incrementing expression into a series of expressions: ++std::get<I>(iterators)...). Dereferencing the iterator will decrement all wrapped iterators and pass it to std::make_tuple (which is also implemented as unpacking the expression *std::get<I>(iterators)...).
P.S. Its implementation is based on a lot of ideas coming from answers to this question.
The implementation of std::vector that ships with Visual Studio 2010 and earlier versions has a well known particularity: the resize method has the following signature (C++03-compliant):
void resize(size_type new_size, value_type value);
instead of the C++11-compliant signature that's been used by the majority of other STL implementations (like gcc's STL or STLport) long before C++11:
void resize(size_type new_size, const value_type& value);
The problem with the first variant is that, in some situations, it will fail to compile if value_type has an alignment specification:
struct __declspec(align(64)) S { ... };
std::vector<S> v; // error C2719: '_Val': formal parameter with __declspec(align('64')) won't be aligned
This is a well known problem with no satisfactory workaround apart from using a different implementation of std::vector.
I'm looking for a well-written, well-tested, self-contained and STL-compatible implementation of std::vector with a MIT-style licence that I could drop into my project as a container of choice for aligned types.
I considered extracting it from STLport or gcc's STL but, being fully standard-compliant, they're both large with many non-trivial dependencies.
(I would be perfectly happy with an implementation of a reasonable subset of std::vector that would only support push_back, clear, capacity, size, reserve, resize, swap and array indexing.)
Any ideas?
The guys behind the Eigen library seem to have found a nice workaround for the problem of storing "overly-aligned types" (as Stephan T. Lavavej call them) into a std::vector as implemented in Visual Studio's STL.
Their implementation seems unnecessary complicated (check the sources here and here) but the main idea is to encapsulate the type that goes into the std::vector with a thin wrapper:
#include <vector>
template <typename T>
struct wrapper : public T
{
wrapper() {}
wrapper(const T& rhs) : T(rhs) {}
};
struct __declspec(align(64)) S
{
float x, y, z, w;
};
int main()
{
std::vector< wrapper<S> > v; // OK, no C2719 error
return 0;
}
About the implementation in Eigen, I must admit I don't quite understand
why they need Eigen::aligned_allocator_indirection,
why they need to make an exception for arithmetic types in EIGEN_WORKAROUND_MSVC_STL_SUPPORT,
why they need to define all these constructors and operators in Eigen::workaround_msvc_stl_support,
or why they need to redefine resize in their partial specialization of std::vector for the Eigen::aligned_allocator_indirection allocator...
Clues welcome. The point is, this trick works perfectly (as far as I can tell) and I don't see anything wrong with it, apart maybe from the slight inelegance.
The easiest (and best, imho) option would be to provide a free function as an extension to the vector interface, which does the right thing. The functions you need to implement resize are all available from the public interface of std::vector:
#include <vector>
template<class T, class Alloc>
void resize(std::vector<T, Alloc>& v,
typename std::vector<T, Alloc>::size_type new_size, T const& val)
{
if (v.size() < new_size)
v.insert(v.end(), new_size - v.size(), val);
else if (new_size < v.size())
v.erase(v.begin() + new_size, v.end());
}
And for consistency also the single argument version:
template<class T, class Alloc>
void resize(std::vector<T, Alloc>& v,
typename std::vector<T, Alloc>::size_type new_size)
{
v.resize(new_size); // simply forward
}
If you, however, just want to drop-in the new vector and never worry about free or member function, another option is to simply subclass std::vector:
#include <vector>
#include <memory>
template<class T, class Alloc = std::allocator<T>>
class myvector
: public std::vector<T, Alloc>
{
typedef std::vector<T, Alloc> base;
public:
typedef typename base::size_type size_type;
void resize(size_type new_size){
base::resize(new_size);
}
void resize(size_type new_size, T const& val){
if (this->size() < new_size)
this->insert(this->end(), new_size - this->size(), val);
else if (new_size < this->size())
this->erase(this->begin() + new_size, this->end());
}
};
Note that I also provided the single argument version of resize, since the two argument version would hide all base-class versions. Also note that I needed to prefix all member function calls with this->, since they are dependent on the base class std::vector, and as such on the template arguments.
My application can only process up to a certain number of entry in the map structure, how do I specify that limit in my code so that my code does not get overwhelmed (for lack of better term). Is there a way to specify the max limit when defining the variable of type map?
Thanks
There's no way to set a limit when instantiating the map, though I supposed you could have your own safe guard when accessing it. For example:
if (mymap.find(a) == mymap.end() and mymap.size() >= MAX_MAP_ALLOWED) {
throw (runtime_error("map limit exceeded"));
} else {
mymap[a] = b;
}
You could possibly create your own map class that encapsulates these checks.
The stl containers also take an 'allocator' as a (defaulted) parameter. This allocator is the container's means to allocate new space for it's data.
If you define a 'capped' allocator (sounds simple, hey?), you're there.
EDIT - In some fora, I found that the allocators, though initially intended stateless, can be statefull on most (modern) compilers. That's why I carry on on this. It's quite cumbersome, though, to do it this way, and probably way more easy and comprenensible to aggregate your map type in a cappedmap adapter.
It took me a lot of moments here and there, but here I got a compiling, capped, example:
// an allocator with maximally MAX elements.
template< typename T, size_t MAX = 5 >
struct AllocateCapped {
// reuses an existing allocator
typedef std::allocator<T> tallocator;
typedef typename tallocator::value_type value_type;
typedef typename tallocator::pointer pointer;
typedef typename tallocator::reference reference;
typedef typename tallocator::const_pointer const_pointer;
typedef typename tallocator::const_reference const_reference;
typedef typename tallocator::size_type size_type;
typedef typename tallocator::difference_type difference_type;
The actual code of the capped allocator delegates to the allocator member:
size_t free;
tallocator allocator;
AllocateCapped():free(MAX){
printf("capped");
}
template<typename T2>
AllocateCapped( const AllocateCapped<T2>& other ){}
pointer allocate( size_type n, const_pointer hint = 0) {
if( !free ) throw std::bad_alloc();
free-=n;
return allocator.allocate( n, hint );
}
void deallocate( pointer p, size_type n ) {
free+=n;
allocator.deallocate(p,n);
}
size_type max_size() const { return free; }
void construct( pointer p, const_reference val ) {
return allocator.construct(p,val);
}
void destroy( pointer p ) { allocator.destroy(p); }
template<class _Other>
struct rebind
{ // convert this type to _ALLOCATOR<_Other>
typedef typename AllocateCapped<_Other> other;
};
};
This allocator can be used like this:
// example structure
struct s {
int i;
s():i(){}
s(int i):i(i){}
};
int main(int argc, char* argv[]) {
typedef AllocateCapped< std::pair<const int, s> > talloc;
talloc a;
talloc::pointer p = reinterpret_cast<talloc::pointer>( a.allocate(1,0) );
a.construct(p, talloc::value_type() );
a.destroy(p);
a.deallocate(p, 1 );
std::map<int , s, std::less<int>, talloc > m;
std::vector<int, AllocateCapped<int> > v;
for( int i = 0; i != 4; ++i ) {
m[i]=s(i);
v.push_back(i);
}
m[5]=s(5); // throws
v.push_back(5); // throws
return 0;
}
Note: not thoroughly tested. It's just an idea.
After trying out the idea of a capped allocator, I think it's way more straightforward to aggregate an std::map (note: not inherit-from! At least not publicly) in a cappedadaptor.
template<typename tKey, typename tVal> class cappedmap {
typedef std::map<tKey,tVal> tmap;
tmap mymap;
cappedmap(size_t amax):mymax(amax){}
// adapt the map interface
pair<tmap::iterator,bool> insert( tmap::value_type kv ) {
if( mymap.size() > mymax ) throw myexcept();
return mymap.insert(kv);
}
tVal operator[]( tKey k ) {
tVal v = mymap[k];
if( mymap.size() > mymax ) {
mymap.remove(k)
throw myexcept();
}
}
...
};
I need an array where size is known at compile time. I know I can use std::vector or boost::array. But that's doesn't teach me how it works internally. Also I couldn't find how to add items into boost::array other than using the initializer. I have written the following code for a generic array. My intention is to get familiar with iterators, template specializations etc. Following is the code
template<typename T>
struct iterator_traits
{
typedef T value_type;
typedef T& reference_type;
typedef T* iterator;
typedef const T* const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
};
template<typename T>
struct iterator_traits<T*>
{
typedef T* value_type;
typedef T*& reference_type;
typedef T** iterator;
typedef const T const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
};
template<typename T, size_t size = 10>
class Array
{
public:
typedef typename iterator_traits<T>::value_type value_type;
typedef typename iterator_traits<T>::reference_type reference_type;
typedef typename iterator_traits<T>::iterator iterator;
typedef typename iterator_traits<T>::const_iterator const_iterator;
typedef typename iterator_traits<T>::reverse_iterator reverse_iterator;
Array() : lastIndex(0) {
}
void add(value_type element) {
if(lastIndex >= size)
throw std::out_of_range("Array is full");
array_[lastIndex++] = element;
}
reference_type at(unsigned int index){
if(index < size)
return array_[index];
else
throw std::out_of_range("Invalid index");
}
size_t capacity(){
return size;
}
iterator begin(){
return array_;
}
iterator end(){
return array_ + size;
}
const_iterator begin() const{
return array_;
}
const_iterator end() const{
return array_ + size;
}
reverse_iterator rbegin() {
return reverse_iterator(end());
}
reverse_iterator rend() {
return reverse_iterator(begin());
}
private:
value_type array_[size];
unsigned int lastIndex;
};
The above code works well. Following are my questions
1 - How can I create my array like boost::array does? Something like
Array<int> ints = { 10, 12 };
2 - Are there any pitfalls in the code?
3 - I had to use a specialization for pointer types in traits. Is that the best practice?
4 - Iterator pattern is implemented correctly or not?
Any thoughts would be great!
1 - How can I create my array like boost::array does? Something like
Array<int> ints = { 10, 12 };
In c++, you can only (currently) use a braces enclosed initializer list if your struct, union or c-style array meets the criteria of being an aggregate. To do such, according to the standard:
8.5.1.1 An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
You would have to make your class have those features if you wanted to use them in the current standard. The next standard (see here) includes a provision to allow other types to do so.
2 - Are there any pitfalls in the code?
Here is one: the reason you can't add things to a boost list is it always has the same number of elements (the size it was allocated with). In your array you can add elements, but you've still constructed 10 elements underneath the hood during construction. this could lead to some suprising results if the user isn't expecting the default constructor called 10 times.
boost::array uses a feature of the language: A struct with no constructors can use an initialization list. If you provide your own constructor, you can't use an initialization list.
Also, you're using iterator_traits wrong. Your code should be something like
// type definitions
typedef T value_type;
typedef T* iterator;
typedef const T* const_iterator;
typedef T& reference;
typedef const T& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
iterator_traits is for traits of iterators. Also, you can just use a pointer as an iterator. The STL explicitly allows this.
2 - are there any pitfalls ?
I would get rid of the default size "size = 10". What makes 10 the default size of an array ? What I see happening instead is someone accidentally leaving off the size, and assuming that its bigger than it is.
Regarding adding elements and the C-style initialization, I don't believe its possible to do both. It works in boost because (I believe) the object is, quite simply, an array under the covers. It can't dynamically re-size. The class simply adds iterators (and other sugar like ::at()) to a plain array.