views:

347

answers:

1

I'm writing a game and an accompanying engine in C++. The engine relies heavily on automation using a simple embedded scripting language. Scripts can create object classes, define event listeners on them, and produce instances from them. At present, an instance must be bound to a script-global identifier in order to preserve its existence. The obvious result of this is that there can be no anonymous objects, which will be by far the most common.

At present, instances are managed using a std::set<Instance*, spatial_sort>, where spatial_sort is a functor that sorts instances by position, for rendering and collision detection. Instances are removed and re-inserted each frame using their current position as a hint, under the assumption that they're not likely to move a whole lot in a fiftieth of a second. If a dead flag is set in the instance, it is erased from the set. The Instance constructors and destructor invoke insert(this) and erase(this), respectively.

In order to allow anonymous instances, I want to change the set to a std::set<boost::shared_ptr<Instance>, spatial_sort>, which would allow Instance to share ownership of instances and preserve their existence until they destroy themselves. Unfortunately, because the calls to insert() need to be placed in the constructor, shared_from_this() won't work for obtaining a shared_ptr to the Instance. It doesn't matter at all that Instance happens to already inherit from boost::enable_shared_from_this<> via its base class.

Can anyone recommend a suitable workaround?

Edit:

I did what I should have been doing in the first place, and split the behaviour of the Instance class into two classes: Instance and Reference. The expression new SomeClass in a script then returns a Reference to a new Instance. The Instance objects themselves are never managed using a shared_ptr, so they are responsible for committing suicide in response to a suitable event, e.g., end of animation, end of level, etc.

Thanks for the help! Refactoring is as good a solution as any if it Just Works.

+2  A: 

You could add a static method to Instance that you then use to create new objects and that also does the administrative stuff like adding it to the set:

static Instance* create(int something) {
  boost::shared_ptr<Instance> sptr(new Instance(something));
  instanceset.insert(sptr);
  return sptr.get();
}

If you want to make this the only way to construct an object of this class you could also make the normal constructor private or protected.

For more on this see also the C++ FAQ Lite entry about "Dynamic binding during initialization", which is not directly related but uses the same technique to work around the restrictions on the use of virtual functions in constructors.

sth
Better yet, return the shared_ptr instead of a raw one.
rlbond
I'm messing with this solution at the moment; basically create() is only ever called to create anonymous instances, and initialises an Instance with an "anonymous" flag set. If this flag is not set, the Instance by default performs an insert() of a shared_ptr to itself with a null deleter. At present I'm trying to track down the mysterious bad_weak_ptr exception that resulted from the change.
Jon Purdy
*"If this flag is not set, the Instance by default performs an insert() of a shared_ptr to itself"* I'm not sure what exactly you are doing, but it sounds like it would run again into the problem that you can't call `shared_from_this()` during construction. Also note that `shared_from_this()` only works if the object actually is stored in a shared_ptr somewhere. For example `shared_from_this()` on a statically allocated instance won't work.
sth
I presumed that using a null deleter would work despite that `shared_from_this()` is invalid, because the `shared_ptr` would not attempt to do anything with its stored pointer. I have learned that constructing two `shared_ptr`s independently to the same object causes problems no matter what, hence the `bad_weak_ptr` exception when the `Instance` was destroyed. My problem is that the `Instance` needs to be entered in the `set` whether it is anonymously created or not, but it needs to be default-constructible and copy-constructible.
Jon Purdy