Reputation: 9445
I have an object -a scheduler class-. This scheduler class is given member function pointers, times and the pointer to the object which created the scheduler.
This means I could do something as much as: (pObject->*h.function)(*h.param);
Where pObject is the pointer to the original object, h a class which contains the function
+ void pointer parameter
so I can pass arguments to the original function.
When I like to initialize this object I have the explicit Scheduler(pObjType o);
constructor (where pObjType is a template parameter).
When I create an object which should have this alarm I type:
struct A {
typedef void (A::*A_FN2)(void*);
typedef Scheduler<A*,A_FN2> AlarmType;
A(int _x) : alarm(NULL)
{
alarm.SetObject(this);
}
AlarmType alarm
However this alarm-type puts quite a big limitation on the object: if I forget to add a copy-constructor (to A) the class would get undefined behaviour. The scheduler would keep pointing to the original object, and that original object might go out of scope, or even worse, might not.
Is there a method that when I copy my alarm (by default, so in the scheduler's copy-constructor) I can get the calling object (and pointer to that?)?
Or if that isn't possible, is it possible to throw a (compile) error if I forget to implement a copy-constructor for my structure? - And try to copy this structure somewhere?
Upvotes: 2
Views: 2808
Reputation: 3697
The simplest way to prevent the specific issue you're asking about is to make Scheduler
noncopyable (e.g. with boost::noncopyable
). This means that any client class incorporating a value member of type Scheduler
will fail to be copyable. The hope is that this provides a hint for the programmer to check the docs and figure out the copy semantics of Scheduler
(i.e. construct a new Scheduler
for every new A
), but it's possible for someone to get this wrong if they work around the problem by just holding the Scheduler
by pointer. Aliasing the pointer gives exactly the same problem as default-copy-constructing the Scheduler
instance that holds a pointer.
Any time you have raw pointers you have to have a policy on object lifetime. You want to ensure that the lifetime of any class A
is at least as long as the corresponding instance of Scheduler
, and as I see it there are three ways to ensure this:
A
contains a Scheduler
, so Scheduler
can't contain an A
A
instances, e.g. requiring that they are always held by smart pointer, or that cleaning them up is the responsibility of some class that also knows to clean up the Scheduler
s that depend on themThe CRTP could work like this:
#include <iostream>
using namespace std;
template<typename T>
struct Scheduler {
typedef void (T::* MemFuncPtr)(void);
Scheduler(MemFuncPtr action) :
action(action)
{
}
private:
void doAction()
{
this->*action();
}
MemFuncPtr action;
};
struct Alarm : private Scheduler<Alarm> {
Alarm() : Scheduler<Alarm>(&Alarm::doStuff)
{
}
void doStuff()
{
cout << "Doing stuff" << endl;
}
};
Note that private inheritance ensures that clients of the Alarm
class can't treat it as a raw Scheduler
.
Upvotes: 0
Reputation: 146968
If you want any kind of automatic copy construction semantics, then you're going to need to go to the CRTP- no other pattern provides for a pointer to the owning object.
The other thing is that you should really use a boost::/std::function<>, they're far more generic and you're going to need that if you want to be able to use Lua functions.
Upvotes: 0
Reputation: 25759
As I see it, you have an opportunity to improve your design here, that may help you get rid of your worry.
boost::NonCopyable
.Upvotes: 1