Thread: operator new overloading and template functions

  1. #1
    Lode Runner
    Join Date
    May 2004
    Posts
    53

    operator new overloading and template functions

    I'm trying to make a templated version of a memory pool.
    The nicest way to do this, would be to overload the new operator, allowing me to give the memory pool as a parameter of the operator.

    Here is my current code:
    Code:
    template <class T>
    inline void* operator new(size_t size, MemoryPool& rPool)
    {
      return rPool.Allocate<T>(size);
    }
    This compiles, but I can't find a syntax to call the templated new. Maybe there isn't any...
    There might another way to get type information inside the operator new definition...

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    I believe you use a version of placement new, something like:
    Code:
    sometype *p = new (pool) sometype;
    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Lode Runner
    Join Date
    May 2004
    Posts
    53
    Quote Originally Posted by matsp View Post
    I believe you use a version of placement new, something like:
    Code:
    sometype *p = new (pool) sometype;
    That works only in the non-templated case. If the new operator and MemoryPool::Allocate don't use templates, this works like a charm.
    If I use the templated version and call:

    Code:
    sometype *p = new (pool) sometype;
    I get
    Code:
    error: no matching function for call to 'operator new(long unsigned int, MemoryPool&)'
    I've tried:
    Code:
    sometype *p = new (pool) <sometype> sometype;
    
    //and
    
    sometype *p = new <sometype> (pool) sometype;
    
    //and
    
    sometype *p = <sometype> new (pool) sometype;
    None of them compile. Syntax errors for each case.

  4. #4
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Ah, ok, I see what the problem is (but I don't really know how to fix it): Your pool depends on the type of the object you are creating.

    I think you need something like:
    Code:
    sometype *p = new (pool<sometype>) sometype;
    Although, it's probably more that pool needs to be created with the right type, and that particular type of pool passed along.

    I may of course be completely wrong.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  5. #5
    Lode Runner
    Join Date
    May 2004
    Posts
    53
    That's pretty much it. Except I don't want the MemoryPool to be fixed to only one type.

    Is there any other way to get type information inside the new definition?

  6. #6
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    For my benefit, wht does 'rPool.Allocate<T>(size);' do?

    Is there any other way to get type information inside the new definition?
    Several.

    Wrap 'T' for example:

    Code:
    template <class T>
    inline void* operator new(size_t size, MemoryPool& rPool, const wrap<T> & ignored)
    {
      return rPool.Allocate<T>(size);
    }
    Code:
    int * i = new (pool, wrap<int>()) int;
    Soma

  7. #7
    The larch
    Join Date
    May 2006
    Posts
    3,573
    I think the templates are not necessary. operator new returns a void* and doesn't care about the type and the size is passed automatically.

    (new is a two-step thing: first your global operator new is called and then the object is constructed in the void* that was returned. Finally a pointer of the correct type is returned to the caller. Or something like that.)

    Code:
    #include <iostream>
    #include <cstdlib>
    
    class MemoryPool
    {
        public:
        //no templates here
        void* Allocate(size_t size)
        {
            std::cout << "Allocating " << size << '\n';
            return malloc(size); //can't use new?
        }
        //TO DO: Dealloc
    };
    
    inline void* operator new(size_t size, MemoryPool& rPool)
    {
      return rPool.Allocate(size);
    }
    
    //TO DO: operator delete
    
    struct Test
    {
        Test() { std::cout << "Test::Test()\n"; }
        void test() const { std::cout << "I'm OK\n"; }
    };
    
    int main()
    {
        MemoryPool pool;
        Test* n = new (pool) Test;
        n->test();
    }
    You probably should find more reading about it (for example Thinking in C++, vol.1, chapter 13).
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  8. #8
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    I think the templates are not necessary. operator new returns a void* and doesn't care about the type and the size is passed automatically.

    (new is a two-step thing: first your global operator new is called and then the object is constructed in the void* that was returned. Finally a pointer of the correct type is returned to the caller. Or something like that.)
    This is why I'm curious about what it does; from the look of the thing he is trying to construct objects inside 'operator new'. It also seems that he is very close to asking "How do I call "'placement delete'"?".

    Soma

  9. #9
    Lode Runner
    Join Date
    May 2004
    Posts
    53
    Quote Originally Posted by phantomotap View Post
    For my benefit, wht does 'rPool.Allocate<T>(size);' do?
    Actually, this isn't the right syntax. The pool is still work in progress, so I wasn't clear where to put some stuff.
    Basically, I'm storing a pointer to the destructor of the new object so that I can "roll back" when clearing the pool. Without the need for the pool user to explicitly call the destructor.


    EDIT: but this won't work since the destructor doesn't exist yet, inside the "new". I should've thought this through...
    Last edited by krappa; 05-22-2008 at 09:02 AM.

  10. #10
    The larch
    Join Date
    May 2006
    Posts
    3,573
    I didn't even know that you can take the address of the destructor! But if you could, it probably wouldn't matter if you had an instance or not (just like with member function pointers).

    So what are you trying to write - a garbage collector? (If so, perhaps consider smart pointers instead...)
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  11. #11
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Of course you can take the address of it (just like you can ANY function in C or C++), but you also need to remember the instance that it belongs to and then call it with a copy (pointer to) the original instance, as the "this" value needs to be correct when destroying the object.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  12. #12
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Of course you can take the address of it (just like you can ANY function in C or C++), but you also need to remember the instance that it belongs to and then call it with a copy (pointer to) the original instance, as the "this" value needs to be correct when destroying the object.
    Eh, the C++ Standard states in section 12.4: "The address of a destructor shall not be taken."
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  13. #13
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by laserlight View Post
    Eh, the C++ Standard states in section 12.4: "The address of a destructor shall not be taken."
    Ok, fair enough - so don't do that then... ;-)

    In fact, I wasn't even getting a sensible error out of g++ when I tried - but I'm not entirely sure what the syntax would actually be.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  14. #14
    Lode Runner
    Join Date
    May 2004
    Posts
    53
    So since I didn't have the destructor, I decided to wrap the destruction in a static template function:

    Code:
    typedef uint8* (*DtorType)(uint8*);
    
    template <class T> uint8* Dtor(uint8* pToDelete)
    {
      T* pObj = (T*)(pToDelete - sizeof(T));
      pObj->~T();
      return pObj;
    }
    and here is the templated allocation in the mempool. mpCurrent is the current pointer (uint8*) in the pool.
    Code:
     template <class T> void* Allocate()
      {
        if ((mpCurrent + sizeof(T) + sizeof(DtorType)) > (mpMemory + mSize))
        {
          return NULL;
        }
        else
        {
          uint8* pRet = mpCurrent;
          mpCurrent += sizeof(T);
    
          //this line is the problem
          *mpCurrent = &Dtor<T>;
    
          mpCurrent += sizeof(DtorType);
          return pRet;
        }
      }
    This compiles on Apple G++ 4.0.1.
    But if I try to use the new operator, I get an error:
    cannot resolve overloaded function 'Dtor' based on conversion to type 'unsigned char'

    And it doesn't even compile in Visual C++:
    cannot convert from 'uint8 *(__cdecl *)(uint8 *)' to 'uint8'

    I tried replacing uint8* with void*, but I can't perform arithmetic operations on a void* type.

  15. #15
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    I don't understand the code, but you are doing something weird.
    If mpCurrent is defined as uint8*, then you try to assign the address of a function to a uint8 variable?
    And in case you wanted the return value of the destructor, it returns uint8*, not uint8.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. template overloading question
    By plutino in forum C++ Programming
    Replies: 14
    Last Post: 02-27-2009, 02:10 AM
  2. template plus ambiguous overloading!!
    By mynickmynick in forum C++ Programming
    Replies: 4
    Last Post: 08-27-2008, 03:26 AM
  3. Template overloading?
    By cpjust in forum C++ Programming
    Replies: 5
    Last Post: 02-20-2008, 03:21 PM
  4. Replies: 7
    Last Post: 11-10-2007, 05:17 AM
  5. Replies: 2
    Last Post: 01-04-2003, 03:35 AM