Performance is crucial in my application
I need something that works like std::experimental::dynarray, so an array which size is decided at runtime.
So I thought about using a wrapper class for std::vector, giving all its features, but without the possibility to call resize, reserve or push_back. In few words, all the methods to change its size (please remind me if I missed some of them).
So I started writing this class:
CCVector.hpp:
template <typename T>
class CCVector{
public:
CCVector(size_t size);
T &operator[](typename std::vector<T>::size_type idx);
private:
std::vector<T> v;
};
CCVector.cpp:
template<typename T>
CCVector<T>::CCVector(size_t size) : v(size){}
template<typename T>
T& CCVector<T>::operator[](typename std::vector<T>::size_type idx){
return v[idx];
}
But I this point I thought I have to re-implement every method of std::vector that I need! For example begin, end, size etc, and I don't know how to implement all of them...Besides, this is really bad for maintenance: as soon as I need a new method from std::vector I need to re-implement it in CCVector.
All of this because I want fixed size arrays at runtime. How can I solve this without using the non-standard std::experimental::dynarray?
Use private inheritance and then import the functions you want using the using declaration to introduce the names you want into your class.
template<class T>
class MyVector : private std::vector<T>
{
public:
using std::vector<T>::end;
// etc
};
(With private inheritance you don't get the issue with vector not having a virtual destructor which is the reason most people don't like inheriting from standard containers)
Privately inheriting std::vector is as proposed in the other answer is one approach.
There is some memory overhead for using resizable container where a non-resizable would suffice. std::vector is typically around ~4 x size of a data pointer. You can store a pointer to data + length in half of that:
template<class T>
class dynarray
{
std::unique_ptr<T[]> data;
T* end; // or size_t size
//TODO functions
};
The downside obviously is that you must re-implement all of the boilerplate, that you could otherwise inherit from std::vector. That said, all of the boilerplate is quite trivial - there is just lots of it.
You are right that std::vector has quite a few member functions that you need to expose to the outside, but there are not thaaat many. For example, see here http://www.cplusplus.com/reference/vector/vector/
Furthermore, you do not need to (and should not) reimplement std::vector on your own - relying on tested, fast code like your STL is almost always the better option.
So, instead of reimplementing, simply "forward" the functions to the outside, like so:
iterator begin()
{
return v.begin();
}
This is a bit of boilerplate code, but if you do this once for the non-resizing functions of std::vector, you are done.
Edit:
Also, do not inherit from std::vector. This is a bad idea for many reasons.
Okay, I was a bit quick here, sorry for that. As the comments, and this post suggest, inheriting from an STL container is not inherently bad. Particularly if you use private inheritance, it might just be a valid solution for the given task.
Related
I want to create a container that provides all of the same functionality as a std::vector but with the caveat that you cannot add anymore elements once the vector reaches a specified size.
My first thought was to inherit from std::vector and do something like
template <typename T, unsigned int max>
class MyVector : public std::vector<T> {
public:
// override push_back, insert, fill constructor, etc...
// For example:
virtual void push_back(const T& var) override {
if (this.size() < max) vec.push_back(var);
}
};
But then I learned that inheriting STL containers is bad.
Second thought was to create a wrapper class, i.e.
template <typename T, unsigned int max>
class MyVector {
private:
std::vector<T> vec;
public:
// Wrappers for every single std::vector function...
iterator begin() { return vec.begin(); }
const_iterator begin() const { return vec.begin(); }
//and on and on and on...etc.
};
But this smells bad to me. Seems very difficult to maintain.
Am I missing something obvious, should I try an alternative approach?
How can I create a vector with a maximum length?
You cannot do that with std::vector. You can however refrain from inserting any elements after you reach some limit. For example:
if (v.size() < 10) {
// OK, we can insert
}
I want to create a container that provides all of the same functionality as a std::vector but with the caveat that you cannot add anymore elements once the vector reaches a specified size.
That, you can do.
virtual void push_back(const T& var) override {
std::vector::push_back isn't virtual, so you may not override it.
Also, I recommend considering whether the push back should be silently ignored in the case max is reached.
But then I learned that inheriting STL containers is bad.
To be more specific, publicly inheriting them is a bit precarious.
Using private inheritance, you can use using to pick the members that you wish to delegate directly, and implement the differing members manually.
Seems very difficult to maintain.
The standard library doesn't change very often, and the vector class hasn't changed much so far, and changes have been quite simple, so "very difficult" is debatable - although subjective of course.
You could use meta-programming to make it maintainable. But that may be bit of an over-engineered approach.
I have an application that I want to minimize dynamic allocation for efficiency.
I'm leaning forward using boost::static_vector in one of my classes for that reason. I'm not sure yet exactly how big my array will need to be to accommodate most situations but there are always exceptions...
Instead of discarding the input that would require more space than what my static_vector can store, I would like to fall back on std::vector to handle those exceptions.
A pattern that I commonly use in my classes design, is that I provide its input to the constructor to populate its container, then I provide a getter function that returns a const reference to the inner container.
The possibility that the class might use one or another container is causing a mental blind spot in me.
What would be the best way to offer access to the class elements?
I have come up with few ideas but none are truly satisfactory...
Make my class a container using the proxy/facade pattern
I'm too lazy for doing that and this is not practical if I want to apply this static_vector/vector solution everywhere...
Place the burden on the class user
ie.
void foo(HybridContainerObj &ojb) {
if (obj.IsUsingStaticVector()) {
const boost::static_vector<SomeElem> &vRef = obj.getStaticVec();
}
else {
const std::vector<SomeElem> &vRef = obj.getVec();
}
}
I doesn't feel right to me...
Modify boost::static_vector to do exactly what I have in mind
So far, this is my favorite solution...
Use some C++ magic that I am unaware of
Since both container classes do implement the same STL container concept, I wish that something along the lines would be possible:
const auto &vecRef = obj.getVec();
but AFAIK, this isn't possible... right?
So, I'm here to see what others think of that problem and if someone would have an elegant proposal to present the class used container elements to its users...
Greetings
boost::small_vector may be the combination you are looking for. From https://www.boost.org/doc/libs/1_60_0/doc/html/boost/container/small_vector.html:
small_vector is a vector-like container optimized for the case when it
contains few elements. It contains some preallocated elements
in-place, which can avoid the use of dynamic storage allocation when
the actual number of elements is below that preallocated threshold.
What you want is an "abstraction" over both std::vector and boost::static_vector, depending on what operations you want to do on the abstraction, you have different possibilities:
If you want const-access to the container, you can simply use a class that would wrap a raw array since both std::vector and boost::static_vector use a raw array internally. Fortunately for you, C++20 already has such class: std::span (if you don't have C++20, you can use the one from the GSL). This is the preferred option since it is probably completely transparent in term of performance and the interface of std::span is close to the read-only operations of std::vector or boost::static_vector.
If you want more operations (e.g. being able to push_back, etc.), you'd need a proper abstraction similar to what std::function does for functor objects, which requires a lot more work:
namespace details {
template <class T>
struct container_wrapper {
// All the operations you need.
virtual void push_back(T const& t) = 0;
};
template <class T>
class vector_wrapper: public container_wrapper<T> {
std::vector<T> *m_Vec;
public:
vector_wrapper(std::vector<T> &vec) : m_Vec{&vec} { }
void push_back(T const& t) override {
m_Vec->push_back(t);
}
};
template <class T>
struct static_vector_wrapper: public container_wrapper<T> {
boost::static_vector<T> *m_Vec;
public:
vector_wrapper(boost::static_vector<T> &vec) : m_Vec{&vec} { }
void push_back(T const& t) override {
m_Vec->push_back(t);
}
};
}
template <class T>
class my_container {
std::unique_ptr<details::container_wrapper> m_Wrapper;
public:
my_container(std::vector<T> &vec) :
m_Wrapper(std::make_unique<details::vector_wrapper<T>>(vec)) { }
my_container(boost::static_vector<T> &vec) :
m_Wrapper(std::make_unique<details::static_vector_wrapper<T>>(vec)) { }
};
I have two classes
class Base{ ... };
class Derived : public Base { ... };
and some other function which operates and modifies a collection of Base pointers:
void foo(std::vector<Base *> ptrs_to_base);
I have a std::vector<Derived> which holds my data. To pass it to foo I can construct a new vector of type std::vector<Base *> containing all pointers. In many situations this will be the easiest thing to do. However, I don't like the overhead with which it comes and in some situations it is simply not affordable to allocate dynamic memory.
Is there a way to change foo's interface as to do everything in place? In principle there should be no need for unnecessary construction of collections. I know there is no easy way, so usage of additional (multi-purpose) helper classes would be fine.
For example
template <typename Iter>
void foo(Iter begin, Iter end) {
//*begin is of type Base*
...
}
std::vector<Derived> elements;
std::vector<Derived *> ptrs_to_elements = ... // construct from elements
foo(ptrs_to_elements.begin(), ptrs_to_elements.end());
would be one solution which requires templates and doesn't make it obvious that only pointers of type Base * are required. Edit: W.F. gave a way to recognize wrong usages of this template. It still requires the unnecessary construction of ptrs_to_elements.
The problem is that your function takes a vector as parameter. And vector as any other standard container does not implement a simple interface and is not intended to be derived.
That means that if you only have a vector of objects and need a vector of pointers your only choice is to build a new vector.
For that reason, standard C++ container are very good to store objects, but public interface should rather use... interfaces of simple wrappers around them to make on flight transformation easier (if not simply just possible)
I'm porting some Fortran90 code to C++ (because I'm stupid, to save the "Why?!").
Fortran allows specification of ranges on arrays, in particular, starting at negative values, eg
double precision :: NameOfArray(FirstSize, -3:3)
I can write this in C++ as something like
std::array<std::array<double, 7>, FirstSize> NameOfArray;
but now I have to index like NameOfArray[0:FirstSize-1][0:6]. If I want to index using the Fortran style index, I can write perhaps
template <typename T, size_t N, int start>
class customArray
{
public:
T& operator[](const int idx) { return data_[idx+start]; }
private:
std::array<T,N> data_;
}
and then
customArray<double, 7, -3> NameOfArray;
NameOfArray[-3] = 5.2;
NameOfArray[3] = 2.5;
NameOfArray[4] = 3.14; // This is out of bounds,
// despite being a std::array of 7 elements
So - the general idea is "Don't inherit from std::'container class here'".
My understanding is that this is because, for example, std::vector does not have a virtual destructor, and so should not (can not?) be used polymorphically.
Is there some other way I can use a std::array, std::vector, etc, and get their functions 'for free', whilst overriding specific functions?
template<typename T, size_t N>
T& std::array<T,N>::operator[](const int idx) { ... };
might allow me to override the operator, but it won't give me access to knowledge about a custom start point - making it completely pointless. Additionally, if I were to optimistically think all my customArray objects would have the same offset, I could hardcode that value - but then my std::array is broken (I think).
How can I get around this? (Ignoring the simple answer - don't - just write myArray[idx-3] as needed)
There's no problem with inheriting standard containers. This is only generally discouraged because this imposes several limitations and such an inheritance is not the way how inheritance was originally predicted in C++ to be used. If you are careful and aware of these limitations, you can safely use inheritance here.
You just need to remember that this is not subclassing and what this really means. In particular, you shouldn't use pointers or references to the object of this class. The problem might be if you pass a value of MyVector<x>* where vector<x>* was expected. You should also never create such objects as dynamic (using new), and therefore also delete these objects through the pointer to the base class - simply because destructor call will not forward to your class's destructor, as it's not virtual.
There's no possibility to prevent casting of the "derived pointer" to the "base pointer", but you can prevent taking a pointer from an object by overloading the & operator. You can also prevent creating objects of this class dynamically by declaring an in-class operator new in private section (or = delete should work as well).
Don't also think about private inheritance. This is merely like containing this thing as a field in private section, except the accessor name.
A range converter class could be the solution although you would need to make it yourself, but it would allow you to get the range size to initialize the vector and to do the conversion.
Untested code:
struct RangeConv // [start,end[
{
int start, end;
RangeConv(int s, int e) : start(s), end(e) { }
int size() const { return end - start; }
int operator()(int i) { return i - start; } // possibly check whether in range
}
RangeConv r(-3, 3);
std::vector<int> v(r.size());
v[r(-3)] = 5;
so should not (can not?) be used polymorphically.
Don't give up too soon. There are basically two issues to consider with inheritance in C++.
Lifetime
Such objects, derived classes with non-virtual destructors in the base, can be used safely in a polymorphic fashion, if you basically follow one simple rule: don't use delete anywhere. This naturally means that you cannot use new. You generally should be avoiding new and raw pointers in modern C++ anyway. shared_ptr will do the right thing, i.e. safely call the correct destructor, as long as you use make_shared:
std:: shared_ptr<Base> bp = std:: make_shared<Derived>( /* constructor args */ );
The type parameter to make_shared, in this case Derived, not only controls which type is created. It also controls which destructor is called. (Because the underlying shared-pointer object will store an appropriate deleter.)
It's tempting to use unique_ptr, but unfortunately (by default) it will lead to the wrong deleter being used (i.e. it will naively use delete directly on the base pointer). It's unfortunate that, alongside the default unique_ptr, there isn't a much-safer-but-less-efficient unique_ptr_with_nice_deleter built into the standard.
Polymorphism
Even if std::array did have a virtual destructor, this current design would still be very weird. Because operator[] is not virtual, then casting from customArray* to std:: array* would lead to the wrong operator[]. This isn't really a C++-specific issue, it's basically the issue that you shouldn't pretend that customArray isa std:: array.
Instead, just decide that customArray is a separate type. This means you couldn't pass an customArray* to a function expecting std::array* - but are you sure you even want that anyway?
Is there some other way I can use a std::array, std::vector, etc, and get their functions 'for free', whilst overloading specific functions?
This is a good question. You do not want your new type to satisfy isa std::array. You just want it to behave very similar to it. As if you magically copied-and-pasted all the code from std::array to create a new type. And then you want to adjust some things.
Use private inheritance, and using clauses to bring in the code you want:
template <typename T, size_t N, int start>
struct customArray : private std::array<T,N>
{
// first, some functions to 'copy-and-paste' as-is
using std::array<T,N> :: front;
using std::array<T,N> :: begin;
// finally, the functions you wish to modify
T& operator[](const int idx) { return data_[idx+start]; }
}
The private inheritance will block conversions from customArray * to std::array *, and that's what we want.
PS: I have very little experience with private inheritance like this. So many it's not the best solution - any feedback appreciated.
General thought
The recommendation not to inherit from standard vector, is because this kind of construct is often misunderstood, and some people are tempted to make all kind of objects inherit from a vector, just for minor convenience.
But this rule should'nt become a dogma. Especially if your goal is to make a vector class, and if you know what you're doing.
Danger 1: inconsistency
If you have a very important codebase working with vectors in the range 1..size instead of 0..size-1, you could opt for keeping it according to this logic, in order not to add thousands of -1 to indexes, +1 to index displayed, and +1 for sizes.
A valid approach could be to use something like:
template <class T>
class vectorone : public vector<T> {
public:
T& operator[] (typename vector<T>::size_type n) { return vector<T>::operator[] (n-1); }
const T& operator[] (typename vector<T>::size_type n) const { return vector<T>::operator[] (n-1); }
};
But you have to remain consitent accross all the vector interface :
First, there's also a const T& operator[](). If youd don't overload it, you'll end up having wrong behaviour if you have vectors in constant objects.
Then, and it's missing above, theres's also an at() which shall be consitent with []
Then you have to take extreme care with the constructors, as there are many of them, to be sure that your arguments will not be misinterpreted.
So you have free functionality, but there's more work ahead than initially thougt. The option of creating your own object with a more limited interface, and a private vector could in the end be a safer approach.
Danger 2:more inconsistency
The vector indexes are vector<T>::size_type. Unfortunately this type is unsigned. The impact of inherit from vector, but redefine operator[] with signed integer indexes has to be carefully analysed. This can lead to subtle bugs according to the way the indexes are defined.
Conclusions:
There's perhap's more work that you think to offer a consistent std::vector interface. So in the end, having your own class using a private vector could be the safer approach.
You should also consider that your code will be maintained one day by people without fortran background, and they might have wrong assumptions about the [] in your code. Is going native c++ really out of question ?
It doesn't seem that bad to just stick with composition, and write wrappers for the member functions you need. There aren't that many. I'd even be tempted to make the array data member public so you can access it directly when needed, although some people would consider that a bigger no-no than inheriting from a base class without a virtual destructor.
template <typename T, size_t N, int start>
class customArray
{
public:
std::array<T,N> data;
T& operator[](int idx) { return data[idx+start]; }
auto begin() { return data.begin(); }
auto begin() const { return data.begin(); }
auto end() { return data.end(); }
auto end() const { return data.end(); }
auto size() const { return data.size(); }
};
int main() {
customArray<int, 7, -3> a;
a.data.fill(5); // can go through the `data` member...
for (int& i : a) // ...or the wrapper functions (begin/end).
cout << i << endl;
}
I often have classes that are mostly just wrappers around some STL container, like this:
class Foo {
public:
typedef std::vector<whatever> Vec;
typedef Vec::size_type size_type;
const Vec& GetVec() { return vec_; }
size_type size() { return vec_.size() }
private:
Vec vec_;
};
I am not so sure about returning size_type. Often, some function will call size() and pass that value on to another function and that one will use it and maybe pass it on. Now everyone has to include that Foo header, although I'm really just passing some size value around, which should just be unsigned int anyway ...? What is the right thing to do here? Is it best practice to really use size_type everywhere?
It should be vector<>::size_type like you have, this is the most correct way.
That said, I know many people, including myself, will just use size_t instead. Although it's not mandated to be the same, vector<>::size_type is size_t for every implementation I know. You should be fine with that.
STL defines these types as an abstract interface for containers. It is intended to support any type of backing storage. That might be NUMA or disk-backed storage, where size_type and ptr-type are different from those for system memory. Or - in a NUMA architecture - it might be a specific memory node that's fast, and can work with a very small size_type and ptr_type - which is a relevant optimization on many architectures.
At least, that were the design goals, also driven by anticipation what could be platforms supporting C++. Some early concessions also allowed shortcuts for STL implementers that basically disable this flexibility, and I've never worked with an STL implementation that made use of this. I'd say that's because linear memory access has become much less of a problem, and STL development at that level isn't actually easy.
Still, how much does it hurt you? It would be the right thing to do.
Actually, it should be size_t and not unsigned int, for 64-bit compatibility. As wrapper class writer, I would return size_type. As class client, I would cast it to appropriate type (size_t), if it is more convenient.
I am not so sure about returning
size_type. Often, some function will
call size() and pass that value on to
another function and that one will use
it and maybe pass it on. Now everyone
has to include that Foo header...
It's fine to return the size_type, but this doesn't mean another function should necessarily take the same size_type as typedeffed in your class. There exist conversions between integral types. Be brave and just use size_t.
You can't overload functions anyway so that there would be one that works with size of vector, another for size of deque etc, just in case they all happen to be using a different size_type (which the standard probably permits). - But you could also use templates, if possible, to deduce the correct size_type to use from the argument.
One option you could consider is inheriting from std::vector :
typedef std::vector<whatever> Foo_Vec;
class Foo : public Foo_Vec
{
public:
const Foo_Vec &GetVec() { return (Foo_Vec&)*this; }
};
I am by no means saying this is the best approach, as it can introduce issues that wouldn't occur by having a private member or inheriting from private Foo_Vec, as public Foo_Vec exposes all methods on std::vector to Foo. Moreover, std::vector does not have a virtual destructor, so if you attempt to clean up a collection of std::vector's with a Foo tucked in there, it won't get cleaned up entirely. I'm just throwing it out there.
As other answers suggest, you should use size_t or size_type instead of unsigned int for 64-bit compatibility. Otherwise, in a future 64-bit build, your std::vector could have more than 232 items, but the size value would be truncated, leading to bugs.