So I'm currently in the process of writing a memory debugger and to do that I need stl container objects to use an untracked allocator.
I have std::string peppered throughout my entire codebase, so I typedef'd it to use my untracked allocator:
typedef std::basic_string<char, std::char_traits<char>, UntrackedAllocator<char>> String;
Now when I try to do this:
String str { "Some string" };
String copy = str;
I get this error:
/usr/local/include/c++/7.1.0/ext/alloc_traits.h:95:67: error: no matching function for call to 'UntrackedAllocator<char>::UntrackedAllocator(UntrackedAllocator<char>)' { return _Base_type::select_on_container_copy_construction(__a); }
This is what my Untracked Allocator looks like:
#pragma once
#define NOMINMAX
#undef max
template <typename T>
class UntrackedAllocator {
public:
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
public:
template<typename U>
struct rebind {
typedef UntrackedAllocator<U> other;
};
public:
inline explicit UntrackedAllocator() {}
inline ~UntrackedAllocator() {}
inline explicit UntrackedAllocator(UntrackedAllocator const&) {}
template<typename U>
inline explicit UntrackedAllocator(UntrackedAllocator<U> const&) {}
// address
inline pointer address(reference r) {
return &r;
}
inline const_pointer address(const_reference r) {
return &r;
}
// memory allocation
inline pointer allocate(size_type cnt,
typename std::allocator<void>::const_pointer = 0) {
T *ptr = (T*)malloc(cnt * sizeof(T));
return ptr;
}
inline void deallocate(pointer p, size_type cnt) {
free(p);
}
// size
inline size_type max_size() const {
return std::numeric_limits<size_type>::max() / sizeof(T);
}
// construction/destruction
inline void construct(pointer p, const T& t) {
new(p) T(t);
}
inline void destroy(pointer p) {
p->~T();
}
inline bool operator==(UntrackedAllocator const& a) { return this == &a; }
inline bool operator!=(UntrackedAllocator const& a) { return !operator==(a); }
};
This is my first time working with custom allocators so I have no idea what's going on with it. It's incredibly annoyning that I can't do str1 = str2 if one of them uses a custom allocator.
The problem is the declaration of the copy c'tors as explicit.
Changing the UntrackedAllocator copy c'tor to:
inline UntrackedAllocator(UntrackedAllocator const&) {}
Solves the compilation issue and everything works just fine:
int main() {
String str { "13" };
String copy = str;
const char* cstr = str.c_str();
int out = atoi(cstr);
}
This happens because the assignment operator of the std::basic_string that accepts const std::basic_string & requires an implicit copy construction of the allocator.
Related
#include <cstdlib>
#include <memory>
#include <unordered_map>
template <class T>
struct allocator {
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
allocator() = default;
template <class U>
allocator(const allocator<U>&) {}
T* allocate(std::size_t n) const { return (T*)malloc(n); } // debugger breaks here
void deallocate(T* p, std::size_t) const { free(p); }
};
using allocations_map =
std::unordered_map<void*, std::size_t, std::hash<void*>,
std::equal_to<void*>,
allocator<std::pair<void* const, std::size_t>>>;
allocations_map allocations; // heap corruption in the constructor
void* operator new(std::size_t n) {
auto p = malloc(n);
allocations.emplace(p, n);
return p;
}
void operator delete(void* p) noexcept {
allocations.erase(p);
free(p);
}
int main() { std::vector<int> v(5); }
Why do i corrupt the heap in the constructor of allocations_map? The debugger detects the first heap corruption in a malloc call of the custom allocator, called inside the constructor.
Is there a more elegant solution then to write a non-logging custom allocator for allocations_map? The container shall obviously not log its own allocations.
I also tried two singleton approaches, as suggested in the comments, without success:
allocations_map& get_allocations_map()
{
static allocations_map* allocations_ptr = nullptr;
if (allocations_ptr == nullptr)
{
allocations_ptr = (allocations_map*) malloc(sizeof(allocations_map));
allocations_ptr = new(allocations_ptr)allocations_map;
}
return *allocations_ptr;
}
allocations_map& get_allocations_map()
{
static allocations_map allocations;
return allocations;
}
From std::allocator::allocate allocator allocates n "things" not n bytes. You should change:
T* allocate(std::size_t n) const { return (T*)malloc(n); }
to:
T* allocate(std::size_t n) const { return (T*)malloc(sizeof(T) * n); }
Why do i corrupt the heap in the constructor of allocations_map?
Because the constructor of elements stored in that map access allocated memory out-of-bounds.
My program has several type of small objects to be created and destroyed very frequently in each thread using make_shared, and the shared_ptr will not be passed to another thread, in which case, I decide to write a custom allocate_shared allocator with a boost::pool as its member to allocate fixed size of memory according to the type.
My code is as follows:
ObjectAllocator.h:
#include <boost/pool/pool.hpp>
template<typename T>
class ObjectAllocator
{
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
auto static constexpr block_size=64+sizeof(value_type);
public:
ObjectAllocator() noexcept:pool_(block_size){}
ObjectAllocator(const ObjectAllocator &other) noexcept :pool_(block_size){}
~ObjectAllocator()=default;
template<typename U>
ObjectAllocator(const ObjectAllocator<U> &other) noexcept :pool_(block_size){}
template<typename U>
ObjectAllocator& operator= (const ObjectAllocator<U> &other){
return *this;
}
ObjectAllocator<T>& operator = (const ObjectAllocator &other){
return *this;
}
template<typename U>
struct rebind{ typedef ObjectAllocator<U> other; };
T *allocate(size_type n, const void *hint=nullptr){
#ifdef _DEBUG
assert(n==1);
#endif
return static_cast<T*>(pool_.malloc());
}
void deallocate(T *ptr, size_type n){
#ifdef _DEBUG
assert(n==1);
#endif
pool_.free(ptr);
}
private:
boost::pool<> ObjectAllocator<T>::pool_(block_size);
}
template<typename T, typename U>
inline bool operator == (const ObjectAllocator<T>&, const ObjectAllocator<U>&){
return true;
}
template<typename T, typename U>
inline bool operator != (const ObjectAllocator<T>& a, const ObjectAllocator<U> &b){
return !(a==b);
}
namespace Allocator {
template <typename T>
thread_local ObjectAllocator<T> allocator;
}
main.cpp:
class ObjectA{
public:
int s=0;
void func(){
std::cout<<s<<std::endl;
}
ObjectA() {//std::cout<<"()"<<std::endl;}
~ObjectA() {//std::cout<<"~"<<std::endl;}
};
std::vector<std::shared_ptr<ObjectA>> vec;
void test(){
static uint32_t loop_count=1000*1000;
for(uint32_t i=0;i<loop_count;i++){
shared_ptr<ObjectA> packet = allocate_shared<ObjectA, ObjectAllocator<ObjectA>>(Allocator::allocator<ObjectA>);
vec.push_back(packet);
}
vec.clear();
}
std::vector<std::shared_ptr<ObjectA>> vec2;
void test2(){
static uint32_t loop_count=1000*1000;
for(uint32_t i=0;i<loop_count;i++){
shared_ptr<ObjectA> packet = allocate_shared<ObjectA, ObjectAllocator<ObjectA>>(Allocator::allocator<ObjectA>);
vec2.push_back(packet);
}
vec2.clear();
}
int main() {
std::thread thread1(test);
test2();
return 0;
}
When I try to test it, it crashs and I have no idea why.
Could anyone helps to make it correct? Thanks in advance.
The debugger says seg fault in shared_ptr_base.h
void* _M_get_deleter(const std::type_info& __ti) const noexcept { return _M_pi ? _M_pi->_M_get_deleter(__ti) : nullptr; }
When I try to make boost::pool static, it works fine in single thread and crashes in multi-thread
The debugger says seg fault in shared_ptr_base.h
: _M_use_count(1), _M_weak_count(1) { }
update:
I make boost::pool to be static thread_local and it works properly now
template<typename T>
class ObjectAllocator
{
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
auto static constexpr block_size=64+sizeof(value_type);
public:
ObjectAllocator() noexcept{}
ObjectAllocator(const ObjectAllocator &other) noexcept {}
~ObjectAllocator()=default;
template<typename U>
ObjectAllocator(const ObjectAllocator<U> &other) noexcept {}
template<typename U>
ObjectAllocator& operator= (const ObjectAllocator<U> &other){
return *this;
}
ObjectAllocator<T>& operator = (const ObjectAllocator &other){
return *this;
}
template<typename U>
struct rebind{ typedef ObjectAllocator<U> other; };
T *allocate(size_type n, const void *hint=nullptr){
#ifdef _DEBUG
assert(n==1);
#endif
return static_cast<T*>(pool_.malloc());
}
void deallocate(T *ptr, size_type n){
#ifdef _DEBUG
assert(n==1);
#endif
pool_.free(ptr);
}
private:
thread_local static boost::pool<> pool_;
};
template<typename T>
thread_local boost::pool<> ObjectAllocator<T>::pool_(block_size);
template<typename T, typename U>
inline bool operator == (const ObjectAllocator<T>&, const ObjectAllocator<U>&){
return true;
}
template<typename T, typename U>
inline bool operator != (const ObjectAllocator<T>& a, const ObjectAllocator<U> &b){
return !(a==b);
}
namespace Allocator {
template <typename T>
thread_local static ObjectAllocator<T> allocator;
}
template <typename T, typename ...Args>
inline auto custom_make_shared(Args... args){
return std::allocate_shared<T,ObjectAllocator<T>>(Allocator::allocator<T>,std::forward<Args>(args)...);
}
Both your copy constructors for ObjectAllocator create a new instance of boost::pool each time they're called.
As std::allocate_shared copies the allocator (cppreference), the instance of ObjectAllocator used to allocate std::shared_ptr gets destructed with it's pool before the shared_ptr is destroyed.
Related question: C++ stateful allocator de-allocate issues
Probably unrelated to you problem, but there are also few other issues:
you don't join thread1 in main. This will call std::terminate and crash you program.
boost::pool<> ObjectAllocator<T>::pool_(block_size); - the ObjectAllocator<T>:: part is superfluous and nonstandard. (afaik accepted only in MSVC)
I am trying to create a custom allocator, but there are compilation problems.
By changing the value of the define from
#define _GLIBCXX_FULLY_DYNAMIC_STRING 0
to
#define _GLIBCXX_FULLY_DYNAMIC_STRING 1
I managed to switch from being unable to compile to being able to compile, but should it be like that? Shouldn't it be a little bit simpler ?
Does any body has experienc on that and already knows how these compilation problems may be solved.
The code the minimal required:
#include <bits/c++config.h>
#define _GLIBCXX_FULLY_DYNAMIC_STRING 0
#include <stdint.h>
#include <stddef.h>
#include <memory>
#include <string>
#include <limits>
typedef int32_t Token;
typedef unsigned char byte;
using namespace std;
template <typename T>
struct Allocator {
public:
static const size_t heapSize=0x1000;
static size_t freePos;
static Token freeT;
static byte m[heapSize];
// http://www.codeproject.com/Articles/4795/C-Standard-Allocator-An-Introduction-and-Implement
typedef T value_type;
typedef value_type* pointer;typedef const value_type* const_pointer;
typedef value_type& reference;typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
template<typename U> struct rebind {typedef Allocator<U> other;};
inline explicit Allocator() {freeT=0;freePos=0;}
inline ~Allocator() {}
inline Allocator(Allocator const&) {} // with explicit it doesn't compile
//template<typename U>
//inline explicit Allocator(Allocator<U> const&) {}
inline pointer address(reference r) {return &r;}
inline const_pointer address(const_reference r) {return &r;}
static inline pointer allocate(size_type cnt, typename std::allocator<void>::const_pointer hint = 0){
return reinterpret_cast<pointer>(::operator new(cnt * sizeof (T)));
/* pointer allocate(size_type n, const_pointer hint = 0 ){
size_t t=freePos;freePos+=sizeof(T)*n;return t;
}
*/
}
static inline void deallocate(pointer p, size_type){
::operator delete(p);
/* pointer deallocate(pointer p,size_type n){
size_t sz=sizeof(T)*n;
*(size_t*)(m+p)=sz;
}
*/
}
inline size_type max_size() const{
return std::numeric_limits<size_type>::max() / sizeof(T);
}
inline void construct(pointer p, const T& t) { new(p) T(t); }
/*
void construct(pointer p, const T& val)
{ new ((T*) p) T(val); }
*/
inline void destroy(pointer p) { p->~T(); }
// void destroy(pointer p) { ((T*)m[p])->~T(); }
inline bool operator==(Allocator const&) {return true;}
inline bool operator!=(Allocator const& a) {return false;}
};
#endif
using namespace std;
typedef std::basic_string< char,std::char_traits<char>,Allocator<char> > String;
int main(){
String s("Nice-the-data-goes-in-my-memory");
return 0;
}
It works fine if you make the comparison operators free functions. In general, relational operators should be free functions so they can convert implicitly on both sides:
template <typename T>
bool operator==(Allocator<T> const &, Allocator<T> const &) { return true; }
template <typename T>
bool operator!=(Allocator<T> const &, Allocator<T> const &) { return false; }
Per suggestion from #BenVoigt in response to my question regarding stack allocated stringstream storage, I designed a stack_allocator (code follows below), and declared a basic_ostringstream type using it.
I am experiencing a strange bug though.
The first character I place into the stream is omitted when I print the resulting string!
Here is an example:
template<typename T, size_t capacity, size_t arr_size>
__thread bool stack_allocator<T, capacity, arr_size>::_used[arr_size] = {};
template<typename T, size_t capacity, size_t arr_size>
__thread T stack_allocator<T, capacity, arr_size>::_buf[capacity][arr_size] = {};
typedef std::basic_ostringstream<char,
std::char_traits<char>,
stack_allocator<char, 1024, 5> > stack_ostringstream;
int main()
{
stack_ostringstream _os;
_os << "hello world";
std::cout << _os.str() << std::endl;
return 0;
}
The resulting output is:
ello world
Can anyone elaborate on what is happening to the first character?
The stack_allocator impl follows: It's pretty simplistic, and I'm sure has lots of room for improvement (not withstanding fixing the bug!)
#include <cstddef>
#include <limits>
#include <bits/allocator.h>
template<typename T, size_t capacity = 1024, size_t arr_size = 5>
class stack_allocator
{
public:
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
inline explicit stack_allocator() { }
template<typename U>
inline explicit stack_allocator(const stack_allocator<U, capacity, arr_size>& that) { }
inline ~stack_allocator() {}
template<typename U>
struct rebind
{
typedef stack_allocator<U, capacity, arr_size> other;
};
inline pointer allocate(size_type cnt, typename std::allocator<void>::const_pointer = 0)
{
if (cnt > capacity)
return reinterpret_cast<pointer>(::operator new(cnt * sizeof (T)));
for (size_t i = 0; i < arr_size; ++i)
{
if (!_used[i])
{
_used[i] = true;
return reinterpret_cast<pointer>(_buf[i]);
}
}
}
inline void deallocate(pointer p, size_type)
{
for (size_t i = 0; i < arr_size; ++i)
{
if (p != _buf[i])
continue;
_used[i] = false;
return;
}
::operator delete(p);
}
inline pointer address(reference r) { return &r; }
inline const_pointer address(const_reference r) { return &r; }
inline size_type max_size() const
{
return std::numeric_limits<size_type>::max() / sizeof(T);
}
inline void construct(pointer p, const T& t) { new(p) T(t); }
inline void destroy(pointer p) { p->~T(); }
inline bool operator==(const stack_allocator&) const { return true; }
inline bool operator!=(const stack_allocator& a) const { return !operator==(a); }
private:
static __thread bool _used[arr_size];
static __thread T _buf[capacity][arr_size];
};
Your allocate function can fall off the end if you allocate more than arr_size items. If you use g++ -Wall it will warn you about those sorts of things.
The other problem is that your _buf array indexes are backwards. It should be static T _buf[arr_size][capacity]; which has the arr_size as the row, not the other order that you have it in the original code which makes the capacity be the first index.
Also as a side note, just avoid identifiers that start with leading _ because some such identifiers are reserved for the implementation and it's easier to never use them than to remember the precise rules. Finally, never include the bits/ headers directly, just use the real headers. In this case, memory. I also had to add includes for <iostream> and <sstream> to get it to compile.
I'm trying to write a custom STL allocator that is derived from std::allocator, but somehow all calls to allocate() go to the base class. I have narrowed it down to this code:
template <typename T> class a : public std::allocator<T> {
public:
T* allocate(size_t n, const void* hint = 0) const {
cout << "yo!";
return 0;
}
};
int main()
{
vector<int, a<int>> v(1000, 42);
return 0;
}
I expect "Yo!" to get printed, followed by some horrible error because I don't actually allocate anything. Instead, the program runs fine and prints nothing. What am I doing wrong?
I get the same results in gcc and VS2008.
You will need to provide a rebind member template and the other stuff that is listed in the allocator requirements in the C++ Standard. For example, you need a template copy constructor which accepts not only allocator<T> but also allocator<U>. For example, one code might do, which a std::list for example is likely to do
template<typename Allocator>
void alloc1chunk(Allocator const& alloc) {
typename Allocator::template rebind<
wrapper<typename Allocator::value_type>
>::other ot(alloc);
// ...
}
The code will fail if there either exist no correct rebind template, or there exist no corresponding copy constructor. You will get nowhere useful with guessing what the requirements are. Sooner or later you will have to do with code that relies on one part of those allocator requirements, and the code will fail because your allocator violates them. I recommend you take a look at them in some working draft your your copy of the Standard in 20.1.5.
In this case, the problem is that I didn't override the rebind member of the allocator. This version works (in VS2008):
template <typename T> class a : public std::allocator<T> {
public:
T* allocate(size_t n, const void* hint = 0) const {
cout << "yo!";
return 0;
}
template <typename U> struct rebind
{
typedef a<U> other;
};
};
int main() {
vector<int, a<int>> v(1000, 42);
return 0;
}
I found this by debugging through the STL headers.
Whether this works or not will be completely dependent on the STL implementation though, so I think that ultimately, Klaim is right in that this shouldn't be done this way.
I have two templates for creating customized allocators; the first works automagically if it is used on a custom type:
template<>
class std::allocator<MY_TYPE>
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef MY_TYPE* pointer;
typedef const MY_TYPE* const_pointer;
typedef MY_TYPE& reference;
typedef const MY_TYPE& const_reference;
typedef MY_TYPE value_type;
template <class U>
struct rebind
{
typedef std::allocator<U> other;
};
pointer allocate(size_type n, std::allocator<void>::const_pointer hint = 0)
{
return reinterpret_cast<pointer>(ALLOC_FUNC(n * sizeof(T)));
}
void construct(pointer p, const_reference val)
{
::new(p) T(val);
}
void destroy(pointer p)
{
p->~T();
}
void deallocate(pointer p, size_type n)
{
FREE_FUNC(p);
}
size_type max_size() const throw()
{
// return ~size_type(0); -- Error, fixed according to Constantin's comment
return std::numeric_limits<size_t>::max()/sizeof(MY_TYPE);
}
};
The second is used when we want to have our own allocator for a predefined type with a standard allocator, for instance char, wchar_t, std::string, etc.:
namespace MY_NAMESPACE
{
template <class T> class allocator;
// specialize for void:
template <>
class allocator<void>
{
public:
typedef void* pointer;
typedef const void* const_pointer;
// reference to void members are impossible.
typedef void value_type;
template <class U>
struct rebind
{
typedef allocator<U> other;
};
};
template <class T>
class allocator
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
template <class U>
struct rebind
{
typedef allocator<U> other;
};
allocator() throw()
{
}
template <class U>
allocator(const allocator<U>& u) throw()
{
}
~allocator() throw()
{
}
pointer address(reference r) const
{
return &r;
}
const_pointer address(const_reference r) const
{
return &r;
}
size_type max_size() const throw()
{
// return ~size_type(0); -- Error, fixed according to Constantin's comment
return std::numeric_limits<size_t>::max()/sizeof(T);
}
pointer allocate(size_type n, allocator<void>::const_pointer hint = 0)
{
return reinterpret_cast<pointer>(ALLOC_FUNC(n * sizeof(T)));
}
void deallocate(pointer p, size_type n)
{
FREE_FUNC(p);
}
void construct(pointer p, const_reference val)
{
::new(p) T(val);
}
void destroy(pointer p)
{
p->~T();
}
};
template <class T1, class T2>
inline
bool operator==(const allocator<T1>& a1, const allocator<T2>& a2) throw()
{
return true;
}
template <class T1, class T2>
inline
bool operator!=(const allocator<T1>& a1, const allocator<T2>& a2) throw()
{
return false;
}
}
The first template above, for your own defined type, does not require any further handling but is used automatically by the standard container classes. The second template requires further work when used on a standard type. For std::string, for example, one have to use the following construct when declaring variables of that type (it is simplest with a typedef):
std::basic_string<char>, std::char_traits<char>, MY_NAMESPACE::allocator<char> >
The following code prints "yo" as expected - what you were seeing was our old friend "undefined behaviour".
#include <iostream>
#include <vector>
using namespace std;
template <typename T> class a : public std::allocator<T> {
public:
T* allocate(size_t n, const void* hint = 0) const {
cout << "yo!";
return new T[10000];
}
};
int main()
{
vector<int, a<int> > v(1000, 42);
return 0;
}
Edit: I just checked out the C++ Standard regarding the default allocator. There is no prohibition on inheriting from it. In fact, as far as I'm aware, there is no such prohibition in any part of the Standard.