tags:

views:

795

answers:

10

I have

class Foo {
....
}

Is there a way for Foo to be able to separate out:

function blah() {
  Foo foo; // on the stack
}

and

function blah() {
  Foo foo* = new Foo(); // on the heap
}

I want Foo to be able to do different things depending on whether it's allocated on the Stack or the Heap.

Edit:

Alof of people have asked me "why do this?"

The answer:

I'm using a ref-counted GC right now. However, I want to have ability to run mark & sweep too. For this, I need to tag a set of "root" pointers -- these are the pointers on the stack. Thus, for each class, I'd like to know whether they're in the stack or in the heap.

A: 

Overload new() for your class. This way you'll be able to tell between heap and stack allocation, but not between stack and static/global.

Seva Alekseyev
+2  A: 

I am not positive what you are asking, but overriding the new operator may be what you are trying to do. As the only safe way to create an object on the heap in C++ is to use the new operator, you can differentiate between objects that exist on the heap versus other forms of memory. Google "overloading new in c++" for more information.

You should, however, consider if differentiating between the two types of memory is really necessary from inside the class. Having an object behave differently depending upon where it is stored sounds like a recipe for disaster if you are not careful!

Mike Koval
Not necessarily true. Consider a vector of these objects. The data for vector may have been allocated from the heap, but the object never had new called on it.
GMan
Constructing objects in a vector calls placement new to construct the object. Now I'm not sure if that means you also need to provide a placement new or not... haven't had to dig that deep before.
Michael Anderson
Placement-`new` cannot be replaced. That said, vector does *not* use placement-`new`. (Or containers, for that matter.) They call the `construct` method of their allocator. (Which typically calls placement-`new`. :P)
GMan
Good point about vectors, though I think you mean arrays? Allocation in an array could be prohibited by making the default constructor private, but that's ugly -- especially if the object otherwise doesn't need params in its constructor.
Dan Breslau
+7  A: 

You need to actually ask us the real question :-) It's apparent to you why you think this is necessary but it almost certainly isn't. In fact, it's almost always a bad idea.

Why do you think you need to do this?

I usually find it's because developers want to delete or not delete the object based on where it was allocated but that's something that should usually be left to the client of your code rather than your code itself.


Update:

Apologies, you've probably found one of the few areas in which what you're asking makes sense. Ideally, you'd override all the memory allocation and de-allocation operators to keep track of what is created and removed from the heap.

However, I'm not sure it's a simple matter of intercepting the new/delete for the class since there could be situations where delete is not called and, since mark/sweep relies on a reference count, you need to be able to intercept pointer assignments for it to work correctly.

Have you thought about how you're going to handle that?

The classic example:

myobject *x = new xclass();
x = 0;

will not result in a delete call.

Also, how will you detect the fact that the pointer to one of your instances is on the stack? The interception of new and delete can let you store whether the object itself is stack or heap-based but I'm at a loss as to how you tell where the pointer is going to be assigned to, especially with code like:

myobject *x1 = new xclass();  // yes, calls new.
myobject *x2 = x;             // no, it doesn't.
paxdiablo
In a user-land mark/sweep garbage collector, I would expect that some kind of smart pointer is provided to contain pointers to collectable objects (in effect, this provides accurate marking). Your code snippets are therefore not legitimate, since they reference a gc object using only a non-gc raw pointer. A "compiler-land" implementation might use conservative marking and analyse the stack directly.
Steve Jessop
Overloading new is not totally reliable. You could malloc() a buffer and placement new (or just simply cast) that to a class. That would still look like a stack-based class, but it's on the heap. IMO you cannot garbage collect things created with new: you'll need your own allocation and pointer wrappers.
AshleysBrain
I plan to use this together with ref-counted smart pointers. That have creation, operator=, and destructor overloaded.The example above would end up being like:MyObject::Ptr x = new MyObject();x = 0; // overloading of operator = causes x to do a ref decrement, which triggers the destructor.
anon
You should try `boost::shared_ptr`, for a more canonical and tested implementation of reference counting.
GMan
+4  A: 

A hacky way to do it:

struct Detect {
   Detect() {
      int i;
      check(&i);
   }

private:
   void check(int *i) {
      int j;
      if ((i < &j) == ((void*)this < (void*)&j))
         std::cout << "Stack" << std::endl;
      else
         std::cout << "Heap" << std::endl;
   }
};

If the object was created on the stack it must live somewhere in the direction of the outer functions stack variables. The heap usually grows from the other side, so that stack and heap would meet somewhere in the middle.

(There are for sure systems where this wouldn't work)

sth
Of course, implementation dependent.
GMan
And not that I would recommend doing this for any real task, just a fun idea that came to mind.
sth
I didn't test it, but this might not work in a multithreaded application.
Nick D
Yeah, I know you know these things, just felt it should be said. :)
GMan
I am also sure he knew that you knew that he knew and was just saying.
Martin York
They all kill themselves after 100 days: http://en.wikipedia.org/wiki/Common_knowledge_%28logic%29
Steve Jessop
Heh, clever. But yeah, I doubt it'd work in a threaded app.
jalf
I actually tried this, in about 2003. Unfortunately one of the systems it doesn't work on is pretty much any C++ compiler with optimisations switched on.
Daniel Earwicker
+1  A: 

The meta question as asked by pax is asked "why would you want to do that" you'll likely get a more informative answer.

Now assuming you're doing this for "a good reason" (perhaps just curiousity) can get this behaviour by overriding operators new and delete, but don't forget to override all 12 variants including:

new, delete, new no throw, delete no throw, new array, delete array, new array no throw, delete array no throw, placement new, placement delete, placement new array, placement delete array.

One thing you can do is put this in a base class and derive from it.

This is kind of a pain, so what different behavior did you want?

Rick
There is one problem - placement new can be used on memory from the stack and from the heap. How to distinguish this?
Tadeusz Kopec
+1  A: 

As mentioned above, you need to control how your object is allocated through overloaded new operator. Watch out for two things however, first the 'placement new' operator that initializes your object inside the memory buffer preallocated by user; second, nothing stops the user from simply casting arbitrary memory buffer into your object type:

char buf[0xff]; (Foo*)buf;

Another way is the fact that most runtimes use a bit more memory than asked when doing heap allocations. They usually place some service structure there to identify proper deallocations by pointer. You could inspect your runtime implementation for these patterns, although it will make your code really unportable, dangerous and unsupportable overkill.

Again, as mentioned above, you really are asking for solution details ("how") when you should ask about the initial problem you devised this solution for ("why").

Inso Reiges
+3  A: 

A more direct, and less intrusive method would be to look up the pointer in the memory region maps (such as /proc/<pid>/maps). Each thread has a region allocated to its stack. Static and global variables will live in the .bss section, constants in a rodata or const segment, and so on.

Matt Joiner
+6  A: 

The answer is no, there is no standard/portable way to do this. Hacks involving overloading the new operator tend to have holes. Hacks that depend on checking pointer addresses are OS specific and heap implementation specific, and may change with future versions of the OS. You may be comfortable with that, but I wouldn't build any sort of system around this behavior.

I would start looking at different ways to accomplish your goal - perhaps you can have a totally different type to serve as the "root" in your scheme, or require the users to (properly) annotate the stack allocated types as such with a special constructor.

Terry Mahaffey
Is the `new` hack unreliability: how do you know if the placement new invoked will place the object on the stack or the heap ?
Matthieu M.
Question is "how" to do it, not "how to do it standardly/portably".
Justicle
+1  A: 

Nope, it can't be done reliably or sensibly.

You may be able to detect when an object is allocated with new by overloading new.

But then what if the object is constructed as a class member, and the owning class is allocated on the heap?

Here's a third code example to add to the two you've got:

class blah {
  Foo foo; // on the stack? Heap? Depends on where the 'blah' is allocated.
};

What about static/global objects? How would you tell them apart from stack/heap ones?

You could look at the address of the object, and use that to determine if it is within the range that defines the stack. But the stack may be resized at runtime.

So really, the best answer is that "there's a reason why mark & sweep GC's aren't used with C++". If you want a proper garbage collector, use a different language, one which supports it.

On the other hand, most experienced C++ programmers find that the need for a garbage collector pretty much vanishes when you learn the necessary techniques for resource management (RAII).

jalf
A: 

It is possible if you compare the value of 'this' with the current value of the stack pointer. If this < sp then you have been allocated in the stack.

Try this out (using gcc in x86-64):

#include <iostream>

class A
{
public:
    A()
    {
        int x;

        asm("movq %1, %%rax;"
            "cmpq %%rsp, %%rax;"
            "jbe Heap;"
            "movl $1,%0;"
            "jmp Done;"
            "Heap:"
            "movl $0,%0;"
            "Done:"
            : "=r" (x)
            : "r" (this)
            );

        std::cout << ( x ? " Stack " : " Heap " )  << std::endl; 
    }
};

class B
{
private:
    A a;
};

int main()
{
    A a;
    A *b = new A;
    A c;
    B x;
    B *y = new B;
    return 0;
}

It should output:

Stack 
Heap 
Stack 
Stack 
Heap
Gianni
Could you retype this asm() part for VC++? I'm having trouble using it under VS2008. Thanks.
AOI Karasu