paul23
paul23

Reputation: 9445

Get (pointer to) calling object

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

Answers (3)

Tim Martin
Tim Martin

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:

  • Use composition - not possible in this case because A contains a Scheduler, so Scheduler can't contain an A
  • Use inheritance, e.g. in the form of the Curiously Recurring Template Pattern (CRTP)
  • Have a global policy on enforcing lifetimes of 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 Schedulers that depend on them

The 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

Puppy
Puppy

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

Johan Kotlinski
Johan Kotlinski

Reputation: 25759

As I see it, you have an opportunity to improve your design here, that may help you get rid of your worry.

  1. It is usually a bad idea to pass around member function pointers. It is better to make your structs inherit from an abstract base class, making the functions you want to customize abstract virtual.
  2. If you don't need copying, it is best to disallow it in the base class. Either by making the copy constructor and operator undefined and private, or by inheriting boost::NonCopyable.

Upvotes: 1

Related Questions