I want to use a custom allocator to allocate memory from a freelist for std::basic_ostringstream
. Here is my custom allocator which I want to use:
template <class Tp>
struct NAlloc {
typedef Tp 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;
NAlloc() = default;
template <class T> NAlloc(const NAlloc<T>&) {}
Tp* allocate(std::size_t n) {
n *= sizeof(Tp);
memoryPool *memPool = memoryPool::GetInstance(10);//get memory pool instance
std::cout << "allocating " << n << " bytes\n";
return static_cast<Tp*>(memPool->allocate(n)); //get memory from pool
}
void deallocate(Tp* p, std::size_t n) {
std::cout << "deallocating " << n*sizeof*p << " bytes\n";
memoryPool *memPool = memoryPool::GetInstance(10);
memPool->deallocate(static_cast<void*>(p));//return memory to pool
}
template<typename U>
struct rebind {
typedef NAlloc<U> other;
};
Then, I use it like this:
typedef std::basic_string<char, std::char_traits<char>, NAlloc<char>> OstringStream;
****Problem:****
int main()
{
OstringStream os; //Object creation
os << " Hello, this is OstreamStream class with memory pool"; //here I am getting error
}
Error: 'OstringStream {aka std::basic_string<char, std::char_traits<char>, NAlloc<char> >}' is not derived from 'std::basic_ostream<_CharT, _Traits>'
Your OstringStream
type is a typedef of std::basic_string
, not of std::basic_ostream
. That is why you are getting the error from operator<<
. The left-hand operand must be an object derived from std::basic_ostream
, exactly as the error message is saying.
std::basic_ostream
itself does not use an allocator at all. It uses std::basic_streambuf
for all of its I/O. For instance, std::ostringstream
uses std::stringbuf
, which uses the default std::allocator
.
In C++11, std::basic_ostringstream
has an optional Allocator
template parameter, which it passes down to its internal std::basic_stringbuf
. So, you could write your typedef
like this instead:
typedef std::basic_ostringstream<char, std::char_traits<char>, NAlloc<char>> OstringStream;
int main()
{
OstringStream os;
os << " Hello, this is OstringStream with memory pool";
}
In earlier C++ versions, you would have to:
define a typedef of std::basic_stringbuf
that uses your custom allocator instead of the default allocator.
construct a standard std::ostream
object that uses an instance of your custom stringbuf
type.
For example:
typedef std::basic_stringbuf<char, std::char_traits<char>, NAlloc<char> > Stringbuf_NAlloc;
class OstringStream : public Stringbuf_NAlloc, public std::ostream
{
public:
OstringStream() : Stringbuf_NAlloc(std::ios_base::out), std::ostream(this) {}
};
int main()
{
OstringStream os;
os << " Hello, this is OstringStream with memory pool";
}
In either case, know that the os.str()
method will no longer return a standard std::string
, which uses the default allocator. It will return a std::basic_string
that uses your custom allocator instead. That will cause problems when trying to assign the return value of os.str()
to a standard std::string
, eg:
std::string s = os.str(); // error!
error: conversion from ‘std::__cxx11::basic_ostringstream<char, std::char_traits<char>, NAlloc>::__string_type {aka std::__cxx11::basic_string<char, std::char_traits<char>, NAlloc>}’ to non-scalar type ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’ requested
So be aware of that. The STL is not very flexible when it comes to mixing allocators, so if you use a custom allocator, you usually have to apply it to every type of data container you use from the STL.