operator new overloading and template functions

This is a discussion on operator new overloading and template functions within the C++ Programming forums, part of the General Programming Boards category; Yes, you need a cast: Code: //this line is the problem *mpCurrent = reinterpret_cast(T::uint8 *(*)(uin8*))&Dtor<T>; -- Mats...

  1. #16
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Yes, you need a cast:
    Code:
    //this line is the problem
          *mpCurrent = reinterpret_cast(T::uint8 *(*)(uin8*))&Dtor<T>;
    --
    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.

  2. #17
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,665
    Unless I misunderstand, isn't that the whole problem?
    *mpCurrent is uint8, not uint8* (*)(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.

  3. #18
    Lode Runner
    Join Date
    May 2004
    Posts
    53
    Quote Originally Posted by Elysia View Post
    If mpCurrent is defined as uint8*, then you try to assign the address of a function to a uint8 variable?
    I use uint8* because I'm considering the memory as a field of bytes. It's not a good approach, I'll grant you that.

    I started out using void*, but you can't perform arithmetic on void*. So I couldn't increment the pointer when I was filling up the memory.
    What I want to do is: put an object in the memory, increment the pointer of the size of the object, put a pointer to a static function in memory, increment the pointer of the size of the pointer to the function.

    After a while of using this, my memory should look like this:
    | Type1 obj1 | &Dtor<Type1> | Type2 obj2 | &Dtor<Type2> |

    When I decide to clear my pool, I can just roll back on the Dtor functions, calling destructors for all the objects.

    My problem here is that I can't just consider a pointer as an arithmetic value (which it has to be, in some way).

  4. #19
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Elysia View Post
    Unless I misunderstand, isn't that the whole problem?
    *mpCurrent is uint8, not uint8* (*)(uint8*).
    Oh, yes, of course. We would also have to create a pointer to function pointer that aliases the mpCurrent address (It's possible to do with a cast, but it gets REALLY messy, so it's much easier to read the code if you do two lines of it, one making a pointer to function pointer variable and sets it to mpcurrent (with a reinterpret_cast), the other that assigns this variabel with the Dtor function - and you don't need a cast on this line).

    --
    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. #20
    The larch
    Join Date
    May 2006
    Posts
    3,573
    After a while of using this, my memory should look like this:
    | Type1 obj1 | &Dtor<Type1> | Type2 obj2 | &Dtor<Type2> |

    When I decide to clear my pool, I can just roll back on the Dtor functions, calling destructors for all the objects.
    When you decide to clear your pool, wouldn't you just be looking at large typeless char buffer? How do you plan to know where one object ends and another starts?

    I mean, wouldn't the user code have to tell your pool the location and size of what to look for? If so, wouldn't it negate any usefulness this whole thing might have (automatic destruction of objects??) - as it would be a lot simpler to call normal new and delete?
    Last edited by anon; 05-22-2008 at 11:36 AM.
    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).

  6. #21
    Lode Runner
    Join Date
    May 2004
    Posts
    53
    Quote Originally Posted by anon View Post
    I mean, wouldn't the user code have to tell your pool the location and size of what to look for? If so, wouldn't it negate any usefulness this whole thing might have (automatic destruction of objects??) - as it would be a lot simpler to call normal new and delete?
    That's the point of having the Dtor function return the freed memory pointer. Any time a call a Dtor, I can update my pointer to be right after the next Dtor. I can roll back object destructors one by one, using that trick, without knowing the type at deallocation time, since the Dtor<T> is already pointing to the right place.

  7. #22
    The larch
    Join Date
    May 2006
    Posts
    3,573
    But how would you know how much memory Type1, Type2 etc occupies and where the Dtors are? (You might need to store the size as well.)
    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. #23
    Lode Runner
    Join Date
    May 2004
    Posts
    53
    Quote Originally Posted by anon View Post
    But how would you know how much memory Type1, Type2 etc occupies and where the Dtors are? (You might need to store the size as well.)
    Quote Originally Posted by krappa View Post
    Code:
    typedef uint8* (*DtorType)(uint8*);
    
    template <class T> uint8* Dtor(uint8* pToDelete)
    {
      T* pObj = (T*)(pToDelete - sizeof(T));
      pObj->~T();
      return pObj;
    }
    The Dtor knows the size of Type1, Type2, because of the template.

    When I want to clear the pool, I rewind the current memory pointer by sizeof(DtorType), the pointer now points to a pointer to a templated Dtor function, the templated Dtor knows sizeof(T), I call the Dtor on the current pointer and it returns the new pointer to the free memory.

    Here is the code for the pool cleaning function:
    Code:
    void MemoryPool::Clear(uint8* pLimit)
    {  
      while (mpCurrent > pLimit)
      {
        mpCurrent -= sizeof(DtorType);
        DtorType dtor = *reinterpret_cast<DtorType*>(mpCurrent);
        mpCurrent = dtor(mpCurrent);
      }
    }
    mpCurrent is the pointer to the current location of free memory in the pool.

    This works now. It's missing a couple of features, and could do with some fine tuning but the basics are there. It's been implemented in the NGL/NUI framework. It's LGPL so you can check out the code. It's in nui2 > base > nuiMemoryPool.cpp and include > nuiMemoryPool.h

  9. #24
    The larch
    Join Date
    May 2006
    Posts
    3,573
    OK.

    One thing it needs is disabling copy constructor and assignment operator.

    I understand that the point is to avoid memory leaks easily. However, the public Clear method introduces difficulties of its own: if I want to use it, I have to know at all time the order in which items were added to the memory pool or I'll end up accessing destructed objects.

    Also, couldn't Clear be templated, so the ugly reinterpret_cast would be hidden from the user?

    I also wonder, what is the benefit of introducing verbous allocation code through the new operator, if the same could be probably achieved through a less verbous templated member function:
    Code:
    nuiMemoryPool pool(1024);
    //why not
    Test* pTest = pool.Allocate(Test(100));
    //instead of
    Test* pTest1 = new (pool, Wrapper<Test>()) Test(1);
    Edit: Ok the last point is probably not valid...
    Last edited by anon; 05-23-2008 at 11:55 AM.
    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).

  10. #25
    Lode Runner
    Join Date
    May 2004
    Posts
    53
    Quote Originally Posted by anon View Post
    One thing it needs is disabling copy constructor and assignment operator.
    True.

    Quote Originally Posted by anon View Post
    I understand that the point is to avoid memory leaks easily. However, the public Clear method introduces difficulties of its own: if I want to use it, I have to know at all time the order in which items were added to the memory pool or I'll end up accessing destructed objects.
    Indeed. The Clear method requires to keep the pointer to the first allocated object. I made a helper class, nuiMemoryPoolGuard, to that effect. It just keeps the pointer at the moment it's created, and Clears up to that pointer when it's destroyed. That way you can "stack" allocation contexts using scope.

    Quote Originally Posted by anon View Post
    Also, couldn't Clear be templated, so the ugly reinterpret_cast would be hidden from the user?
    I don't get that... It is hidden, isn't it? The user just has to call Clear, without knowing the type of the objects allocated.

    Quote Originally Posted by anon View Post
    I also wonder, what is the benefit of introducing verbous allocation code through the new operator, if the same could be probably achieved through a less verbous templated member function:
    Code:
    nuiMemoryPool pool(1024);
    //why not
    Test* pTest = pool.Allocate(Test(100));
    //instead of
    Test* pTest1 = new (pool, Wrapper<Test>()) Test(1);
    Wouldn't your example imply copying the contents of the memory into the pool ? Your Test(100) has already been allocated on the heap...

    You *could* Allocate<T>() then call a placement new. But that would introduce possibilities for errors, since you could create something else than what you asked for at that position in memory. Using the new operator avoids this. Also, if you have to use a placement new, it's not so terrible to use another "special" new.

    Also, I've added the new[] operator to the pool, now.
    Last edited by krappa; 05-26-2008 at 04:33 AM.

  11. #26
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Also, couldn't Clear be templated, so the ugly reinterpret_cast would be hidden from the user?
    I don't get that... It is hidden, isn't it? The user just has to call Clear, without knowing the type of the objects allocated.
    I meant a difference between these (ignore the warnings):
    Code:
    void foo(char* p)
    {
        //...
    }
    
    template <class T>
    void bar(T* t)
    {
        char* p = reinterpret_cast<char*>(t);
        //...
    }
    
    int main()
    {
        int* n;
        foo(reinterpret_cast<char*>(n));
        bar(n);
    }
    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).

  12. #27
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,281
    Wouldn't your example imply copying the contents of the memory into the pool?
    Because the type is created and passed to a template method, the method definitely has access to the type information without having to use any additional syntax or wrapper class. The copy constructor could/would then be invoked on the type "contained" in the newly allocated memory.

    Also, if you have to use a placement new, it's not so terrible to use another "special" new.
    *shrug*

    From what I've seen, your requirement of using this 'new' is; I can't call 'delete p' and expect it to do the right thing. (Or at least, it looks that way so far.)

    Soma

  13. #28
    Lode Runner
    Join Date
    May 2004
    Posts
    53
    Quote Originally Posted by phantomotap View Post
    Because the type is created and passed to a template method, the method definitely has access to the type information without having to use any additional syntax or wrapper class. The copy constructor could/would then be invoked on the type "contained" in the newly allocated memory.
    The goal of this memory pool is to have cheap allocations by just incrementing a pointer. Having to create an object, then copy it, then destroy the original would pretty much contravene the original purpose.

    Quote Originally Posted by phantomotap View Post
    From what I've seen, your requirement of using this 'new' is; I can't call 'delete p' and expect it to do the right thing. (Or at least, it looks that way so far.)
    That's true. Normally, you wouldn't need to delete an object on a pool before you want to clear it. But I'm still thinking about how to put the syntax around it.

  14. #29
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,281
    The goal of this memory pool is to have cheap allocations by just incrementing a pointer. Having to create an object, then copy it, then destroy the original would pretty much contravene the original purpose.
    Then you should stop wasting your time. A memory pool is used to impose semantics on allocation/deallocation related to the size or type of memory required. Most memory pool implementations exist only to allow rapid allocation/deallocation for otherwise inexpensive objects. As the cost of construction and destruction grows the cost of allocation/deallocation by comparison becomes negligible. If the construction/destruction of an object is as expensive as allocation/deallocation then the pool would indeed by a waste, but not because the pool would provide no direct benefit; the programmer would need to cache as many such objects as required/possible.

    A memory pool is not a bulk unloader or some sort of garbage collector, so don't use it like that.

    Soma

  15. #30
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    I'm with phantomotap.

    I think you'll find that making use of the regular new/delete feature is, on average, pretty close to "incrementing a pointer". I doubt that your implementation will be sufficently faster to make a big impact. It is probably better in that case, to study what allocatins are done often, and find a way to imrpove those portions of code.

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

Page 2 of 3 FirstFirst 123 LastLast
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, 01: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, 02:21 PM
  4. Replies: 7
    Last Post: 11-10-2007, 04:17 AM
  5. Replies: 2
    Last Post: 01-04-2003, 02:35 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21