1

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>'

Phil Miller
  • 36,389
  • 13
  • 67
  • 90
  • 1
    What's problem are you having with it? – Toby Speight Jan 23 '18 at 16:18
  • If I do this, OstringStream os, os << "Hello" ; std::cout <, NAlloc >}' is not derived from 'std::basic_ostream<_CharT, _Traits>' – user3059007 Jan 23 '18 at 16:24
  • You need to [edit] the question to show your problem (rather than adding a comment). Please write a [mcve] and include it in the body of the question. Thanks. – Toby Speight Jan 23 '18 at 16:30
  • You need to actually ask a question if you want help. Please put it in the question not in a comment. – Justin Randall Jan 23 '18 at 16:30
  • Don't you have to overload the << operator in order to use it on your object? – atru Jan 23 '18 at 18:54
  • I don't think so. I just want to use my custom allocator to handle memory allocation and deallocation. std::basic_ostringstream member function should work (without overloading). I suspect that allocation class for std::basic_ostringstream is not working correctly. – user3059007 Jan 23 '18 at 19:47

1 Answers1

5

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:

  1. define a typedef of std::basic_stringbuf that uses your custom allocator instead of the default allocator.

  2. 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.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • thanks a lot!. I really like your explanation part. I am planning to use custom allocator for basic_string too. – user3059007 Jan 24 '18 at 16:36
  • This works fine for stateless allocators. The ostring_stream() ctor does not take an allocator object, but the stringbuf ctor does. So your example for older C++ looks promising. – brian beuning Feb 14 '19 at 17:36