I would like to create my own allocator class, and I wonder a few things :
I have to define the following types : typedef size_t size_type;and typedef ptrdiff_t difference_type;. Can i use something different than size_t ? If I want to use the STL container in a class that uses uint32_t for lengths and positions instead of size_t, can I use uint32_t instead, or does it have to be size_t ? Can it create overflows or other unpleasant things if i use a different type, or even a signed type ?
How can I passed a parameter to my allocator ? Do I have to add a constructor with a parameter to it, and construct it manually before passing it to a constructor of the STL container to use it with ?
For the function deallocate defined below, can i just ignore the number of items to deallocate ?
void deallocate( pointer p, size_type nNum )
{
(void) nNum;
delete[] p;
}
What is the "rebinding" stuff all about ?
..
template< class U >
struct rebind
{
typedef Allocator< U > other;
};
template< class U >
Allocator( const Allocator< U > & oAlloc )
Thank you. :)
This is your allocator, so you can use the type you want.
But, note, that if you expect to use it with STL containers, you will have to use a type that STL containers expect.
size_t seems appropriate here.
You can provide parameters to your allocator like if it was a normal class.
Constructors, initialization methods or setters are fine.
In fact, you can provide all functionalities you want to your allocator, but you have to respect the allocate, deallocate signature.
You can ignore the size of your deallocate function. I saw a lot of standard allocator implementations that did not use this parameter. This information could be usefull sometimes, for example if your allocator switch to different allocation strategies depending on size, this parameter could be helpfull in deallocate method to switch on the good deallocate implementation.
Related
I'm implementing a super simple container for long term memory management, and the container will have inside an array.
I was wondering, what are the actual implications of those two approaches below?
template<class T, size_t C>
class Container
{
public:
T objects[C];
};
And:
template<class T>
class Container
{
public:
Container(size_t cap)
{
this->objects = new T[cap];
}
~Container()
{
delete[] this->objects;
}
T* objects;
};
Keep in mind that those are minimal examples and I'm not taking into account things like storing the capacity, the virtual size, etc.
If the size of the container is known at compile time, like in first example, you should better use std::array. For instance:
template<class T, size_t C>
class Container
{
public:
std::array<T, C> objects;
};
This has important advantages:
You can get access to its element via std::get, which automatically checks that the access is within bounds, at compile time.
You have iterators for Container::objects, so you can use all the routines of the algorithm library.
The second example has some important drawbacks:
You cannot enforce bounds-check when accessing the elements: this can potentially lead to bugs.
What happens if new in the constructor throws? You have to manage this case properly.
You need a suitable copy constructor and assignment operators.
you need a virtual destructor unless you are sure that nobody derives from the class, see here.
You can avoid all these problems by using a std::vector.
In addition to #francesco's answer:
First example
In your first example, your Container holds a C-style array. If an instance of the Container is created on the stack, the array will be on the stack as well. You might want to read heap vs stack (or similar). So, allocating on the stack can have advantages, but you have to be careful with the size you give to the array (size_t C) in order to avoid a stack overflow.
You should consider using std::array<T,C>.
Second example
Here you hold a pointer of type T which points to a C-style array which you allocate on the heap (it doesn't matter whether you allocate an instance of Container on the stack or on the heap). In this case, you don't need to know the size at compile time, which has obvious advantages in many situations. Also, you can use much greater values for size_t C.
You should consider using std::vector<T>.
Further research
For further research, read on stack vs heap allocation/performance, std::vector and std::array.
Some container A has a template parameter Alloc (that is a template too) representing an allocator type. A specifies Alloc for the type A::Node.
template <template <T> Alloc>
class A {
struct Node {
};
Alloc<Node> allocator_; // the allocator object
};
Please excuse me for possibly wrong C++ code above.
So, allocator_.allocate(1) will allocate sizeof(A::Node) bytes. But during operation, container A needs a memory for some object of other than A::Node type, say a temporary string (of chars).
From technical point of view, I could use existing allocator in such a dirty way:
size_t string_len = 500;
// how much objects spanned in memory is enough to fit our string?
size_t equal_size = (string_len / sizeof(Node)) + 1;
auto mem = allocator_.allocate(equal_size);
char *p = (char*)mem; // reinterpret cast
// ... use p to store the string ... memcpy(p, str_src, str_len); //
// Now p is not needed, so return memory to allocator:
allocator_.deallocate(mem, equal_size);
Is there a less dirty approach, considering I need no more than 1 allocator and I wish to put all the memory management to it?
All this comes from those needs:
to have a single allocator that could be killed to free all (possibly leaked) memory that A is allocated for any its purposes during operation
to have not more than 1 allocator (including the default ::new, ::delete)
std::allocator has a member type rebind for exactly that purpose:
std::allocator<Node> alloc;
std::allocator<Node>::rebind<char>::other char_alloc;
char * mem = char_alloc.allocate(string_len);
You can use an allocator's rebind for this. From this documentation:
A structure that enables an allocator for objects of one type to allocate storage for objects of another type.
it is built exactly for your case - taking an allocator type oriented to one type, and building the corresponding one oriented to some other type.
In your case, it might look like
typename Alloc<T>::rebind<Node>::other allocator_;
You should probably use Alloc::rebind member template to get an allocator for that another object.
However, that does mean that you do have 2 allocators. The advantage of rebind is to allow the user of your template to specify the allocator type only for a single allocated type.
Also note that rebind is optional, so if you must support such allocators, you'll need to pass the other allocator as an argument, but you can still use the rebound allocator as a default value.
is it possible to have some kind of list / array / vector of different structs?
For example in MFC there are CObject and CArray but without MFC:
I can do something alike
std::vector<void*> pVec;
{
MYSTRUCT m = new MYSTRUCT;
pArr.push_back(m);
// looks fine here
}
//what is in my vector now?
Is there something that can handle it?
The obvious preferred approach is to have a std::vector<Base*> to handle polymorphic types.
If you really have completely unrelated types, as it seems to be the case, then boost::any or boost::variant type erasers might be what you are looking for :
std::vector< boost::variant<int, std::string> > vec;
vec.push_back( 21 );
vec.push_back( "hello " );
The example that you have given is dangerous. How do you clean up (i.e. delete) what you've allocated? Its type has been erased by being assigned to a void *, so how can the compiler know which operator delete to call?
You can safely go about this at least two different ways:
All the objects can inherit from a common base which has a virtual destructor. You can then make a std::vector of std::unique_ptrs to the common base. This has the advantage of being extensible (you can declare new types which derive from the common base and throw them in the container without modifying the container), but has the disadvantage of requiring that you go to the free store (to allocate memory for the std::unique_ptrs to point to).
You can use a variant type like boost::variant. This will not require a common base, or allocations on the free store, but it will require that you declare all the types that can possibly be stored in the container when you declare the container (i.e. it's not as easily extensible as an inheritance-based solution).
It seems to be the month of C++ templates for me...
I have a SecureString. SecureString looks just like a std::string, except it uses a custom allocator which zeroizes on destruction:
class SecureString
{
public:
typedef std::basic_string< char, std::char_traits<char>, zallocator<char> > SecureStringBase;
typedef zallocator<char>::size_type size_type;
static const size_type npos = static_cast<size_type>(-1);
....
private:
SecureStringBase m_base;
};
The full code for SecureString can be found at http://code.google.com/p/owasp-esapi-cplusplus/source/browse/trunk/esapi/util/SecureString.h; and the code for the allocator can be found at http://code.google.com/p/owasp-esapi-cplusplus/source/browse/trunk/esapi/util/zAllocator.h.
Currently, we have a swap defined that takes a std::string as an argument:
void SecureString::swap(std::string& str)
{
SecureStringBase temp(str.data(), str.size());
m_base.swap(temp);
str = std::string(temp.data(), temp.size());
}
I feel like I'm missing an opportunity in swap because the underlying types only differ by allocators. Can anyone see a way to avoid the temporary? Is it possible to use rebind to make this run faster?
EDIT: SecureString::swap(std::string& str) is now gone. Reference to the function in this thread has been left in place for posterity.
Jeff
Unfortunately... no.
This is not what rebind is for. rebind is used because an allocator is meant to allocate objects of one type, and one type only (std::allocator<T>) in the STL.
However, there is a trick. When you instantiate std::list<T, std::allocator<T>> for example, then the allocator does not have to allocate Ts, it has to allocate some internal structure instead like __list_node<T>, and that is when rebind is put to use, it creates a new allocator, sibling of the precedent (they differ only by the template parameter and likely share the same memory pool under the covers).
In your case however, your allocator and the std::string allocator are different, and thus they cannot exchange memory. So you have to do a copy.
You can optimize the void swap(SecureString&, SecureString&) operation, but not this one.
One question: why not typedef std::string<char, SecureAllocator<char>> SecureString; ?
In a related question I asked about creating a generic container. Using polymorphic templates seems like the right way to go.
However, I can't for the life of me figure out how a destructor should be written. I want the owner of the memory allocated to be the containers even if the example constructor takes in an array of T (along with its dimensions), allocated at some other point.
I would like to be able to do something like
MyContainer<float> blah();
...
delete blah;
and
MyContainer<ComplexObjectType*> complexBlah();
...
delete complexBlah;`
Can I do something like this? Can I do it without smart pointers?
Again, thanks for your input.
I'd recommend if you want to store pointers to complex types, that you use your container as: MyContainer<shared_ptr<SomeComplexType> >, and for primitive types just use MyContainer<float>.
The shared_ptr should take care of deleting the complex type appropriately when it is destructed. And nothing fancy will happen when the primitive type is destructed.
You don't need much of a destructor if you use your container this way. How do you hold your items in the container? Do you use an STL container, or an array on the heap? An STL container would take care of deleting itself. If you delete the array, this would cause the destructor for each element to be executed, and if each element is a shared_ptr, the shared_ptr destructor will delete the pointer it itself is holding.
You most probably do want to use smart pointers here, it really simplifies the problem. However, just as an excercise, it's quite easy to determine if given type is pointer. Rough implementation (could be more elegant, but I dont want to introduce int2type):
typedef char YesType;
typedef char NoType[2];
template<typename T>
struct IsPointer
{
typedef NoType Result;
};
template<typename T>
struct IsPointer<T*>
{
typedef YesType Result;
};
template<typename T>
struct MyContainer
{
~MyContainer()
{
IsPointer<T>::Result r;
Clear(&r);
delete[] data;
}
void Clear(YesType*)
{
for (int i = 0; i < numElements; ++i)
delete data[i];
}
void Clear(NoType*) {}
T* data;
int numElements;
};
It can be done, but this is pretty advanced stuff.
You'll need to use something like the boost MPL library (http://www.boost.org/doc/libs/1_36_0/libs/mpl/doc/index.html) so that you can get MyContainer's destructor to select the right kind of destructing it will need to do on individual items on the container. And you can use the boost TypeTraits library to decide what kind of deleting is required (http://www.boost.org/doc/libs/1_36_0/libs/type_traits/doc/html/index.html). I'm sure it will have a trait that will let you decide if your contained type is a pointer or not, and thus decide how it needs to be destructed. You may need to implement traits yourself for any other types you want to use in MyContainer that have any other specific deletion requirements. Good luck with it! If you solve it, show us how you did it.
If you don't want to go with smart pointers you can try partial template specialisation, it let's you write a template that is only used when you instatiate a container with a pointer type.
delete is used to deallocate memory previously allocated with new. You do not need to use delete here, when blah and complexBlah go out of scope they will automatically be destroyed.
While yrp's answer shows you one way of using template specialization to delete the objects contained if they are pointers, and not if they aren't, this seems like a fragile solution. If you want behavior like this you are better off using Boost Pointer Container libraries, which provide this exact behavior. The reason that the standard library doesn't is because the containers themselves don't know if they control the contained pointer or not - you need to wrap the pointer in a type that does know - ie a smart pointer.