views:

51

answers:

5

Here is my issue. I have a class to create timed events. It takes in:

A function pointer of void (*func)(void* arg)

A void* to the argument

A delay

The issue is I may want to create on-the-fly variables that I dont want to be a static variable in the class, or a global variable. If either of these are not met, I cant do something like:

void doStuff(void *arg)
{
   somebool = *(bool*)arg;
}

void makeIt()
{
bool a = true;
   container->createTimedEvent(doStuff,(void*)&a,5); 
}

That wont work because the bool gets destroyed when the function returns. So I'd have to allocate these on the heap. The issue then becomes, who allocates and who deletes. what I'd like to do is to be able to take in anything, then copy its memory and manage it in the timed event class. But I dont think I can do memcpy since I dont know the tyoe.

What would be a good way to acheive this where the time event is responsible for memory managment.

Thanks

I do not use boost

class AguiTimedEvent {
    void (*onEvent)(void* arg);
    void* argument;
    AguiWidgetBase* caller;
    double timeStamp;
public:
    void call() const;

    bool expired() const;
    AguiWidgetBase* getCaller() const;
    AguiTimedEvent();
    AguiTimedEvent(void(*Timefunc)(void* arg),void* arg, double timeSec, AguiWidgetBase* caller);
};

void AguiWidgetContainer::handleTimedEvents()
{
    for(std::vector<AguiTimedEvent>::iterator it = timedEvents.begin(); it != timedEvents.end();)
    {
        if(it->expired())
        {

            it->call();
            it = timedEvents.erase(it);
        }
        else
            it++;
    }
}

void AguiWidgetBase::createTimedEvent( void (*func)(void* data),void* data,double timeInSec )
{
    if(!getWidgetContainer())
        return;
    getWidgetContainer()->addTimedEvent(AguiTimedEvent(func,data,timeInSec,this));
}


void AguiWidgetContainer::addTimedEvent( const AguiTimedEvent &timedEvent )
{
    timedEvents.push_back(timedEvent);
}
+1  A: 

Why would you not use boost::shared_ptr?

It offers storage duration you require since an underlying object will be destructed only when all shared_ptrs pointing to it will have been destructed.

Also it offers full thread safety.

Keynslug
What about a non-boost solution?
Milo
@Milo: `shared_ptr` is part of TR1 and C++0x. If nothing else, there's nothing stopping you from writing your own smart pointer if you don't want to use a third party library.
James McNellis
Write something like. Simple wrapper of arbitrary pointer type with reference counting. Solution would be much clearer than that of boost cause you're not depending on thread safety at all as i have understood.
Keynslug
+1  A: 

Using C++0x unique_ptr is perfect for the job. This is a future standard, but unique_ptr is already supported under G++ and Visual Studio. For C++98 (current standard), auto_ptr works like a harder to use version of unique_ptr... For C++ TR1 (implemented in Visual Studio and G++), you can use std::tr1::shared_ptr.

Basically, you need a smart pointer. Here's how unique_ptr would work:

unique_ptr<bool> makeIt(){ // More commonly, called a "source"
    bool a = true;
    container->createTimedEvent(doStuff,(void*)&a,5); 
    return new unique_ptr<bool>(a)
}

When you use the code later...

void someFunction(){
    unique_ptr<bool> stuff = makeIt();
} // stuff is deleted here, because unique_ptr deletes 
  // things when they leave their scope

You can also use it as a function "sink"

  void sink(unique_ptr<bool> ptr){
    // Use the pointer somehow
  }

  void somewhereElse(){
      unique_ptr<bool> stuff = makeIt();
      sink(stuff);
      // stuff is now deleted! Stuff points to null now
  }

Aside from that, you can use unique_ptr like a normal pointer, aside from the strange movement rules. There are many smart pointers, unique_ptr is just one of them. shared_ptr is implemented in both Visual Studio and G++ and is the more typical ptr. I personally like to use unique_ptr as often as possible however.

Dragontamer5788
How would this work with tr1::sharedptr?
Milo
std::tr1::shared_ptr is *exactly* boost::shared_ptr (well... an older version of boost::shared_ptr). shared_ptr essentially works like Python's Garbage collection. See the link in his answer for details.
Dragontamer5788
I should add: shared_ptr is easier to use than unique_ptr. But everyone talks about shared_ptr... so the above is a slightly different approach.
Dragontamer5788
A: 

If you can't use boost or tr1, then what I'd do is write my own function that behaves like auto_ptr. In fact that's what I've done on a project here that doesn't have any boost or tr1 access. When all of the events who care about the data are done with it it automatically gets deleted.

miked
auto_ptr is standard C++98
Dragontamer5788
A: 

You can just change your function definition to take in an extra parameter that represents the size of the object passed in. Then just pass the size down. So your new function declarations looks like this:

void (*func)(void* arg, size_t size)

void doStuff(void *arg, size_t size)
{
   somebool = *(bool*)arg;
   memcpy( arg, myStorage, size );
}


void makeIt()
{
   bool a = true;
   container->createTimedEvent(doStuff,(void*)&a,sizeof(bool), 5); 
}

Then you can pass variables that are still on the stack and memcpy them in the timed event class. The only problem is that you don't know the type any more... but that's what happens when you cast to void*

Hope that helps.

rhinoinrepose
A: 

You should re-work your class to use inheritance, not a function pointer.

class AguiEvent {
    virtual void Call() = 0;
    virtual ~AguiEvent() {}
};

class AguiTimedEvent {
    std::auto_ptr<AguiEvent> event;
    double timeSec;
    AguiWidgetBase* caller;
public:
    AguiTimedEvent(std::auto_ptr<AguiEvent> ev, double time, AguiWidgetBase* base)
        : event(ev)
        , timeSec(time)
        , caller(base) {}
    void call() { event->Call(); }

    // All the rest of it
};

void MakeIt() {
    class someclass : AguiEvent {
        bool MahBool;
    public:
        someclass() { MahBool = false; }
        void Call() { 
            // access to MahBool through this.
        }
    };
    something->somefunc(AguiTimedEvent(new someclass())); // problem solved
}
DeadMG