Pass Entities to a C-extension


I’m trying to pass an Entities object to my C-extension, but how to do that is unclear.

Let’s say, for example, I have this code in Ruby:

def onElementRemoved(entities, entity_id)
  @model_manager.entities_on_element_removed(entities, entity_id)

On the C-side, it would look like the following:

void SuModelManager::EntitiesOnElementRemoved(RUBY_VALUE entities, int entity_id) {
  SUEntityRef entities_ref = SU_INVALID;
  SU_CHECK(SUEntityFromRuby(entities, &entities_ref));
  // ... Do Stuff ...

However, Entities are not inheriting from Entity, so this fails with error code SU_ERROR_INVALID_ARGUMENT. Is there a complementary API function to SUEntityFromRuby to handle this case?

Another thing I tried is going rogue via the Ruby C API:

void SuModelManager::EntitiesOnElementRemoved(RUBY_VALUE entities, int entity_id) {
  // The value passed in entities is of type T_DATA which is a void* pointer.
  void* data = rb_data_object_get(entities);
  // Points to something, but probably not what I'm expecting.

I can get a pointer with the above method, but if I cross-check it with the parent of the deleted entity, it points to something else. So I suspect I have a pointer to an opaque internal data structure which is of no use to me.

Any help is appreciated!


By the time most deleted/removed observer callbacks fire, the entity/object has already been deleted. So it, and likely it’s parent are pointing to dummy class objects on the Ruby side and perhaps some dummy struct on the C side.

This is the reason why only the ID is passed. It is expected that extensions will be keeping some collection of their own probably keyed by the integer ID, so this will allow a lookup and allow the extension to deal with the removal.

It is not ideal and so requests have been opened in the API tracker. See what I said yesterday in this post …

FYI: The entities objects in Ruby are so called “thinly wrapped” C++ collections.

No. But I think you may be able to work around it. If you keep a hash of objects and their parents keyed by ID. Then pass the parent entity (model or component definition for groups and instances) and on the C-side get the entities collection via the normal C API functions:

It can be more observer code keeping track of everything.

Thanks Dan, for the detailed explanation!

I can work around the problem by maintaining extra state, or explicitly passing the parent of the Entities, which is either a model or component definition.

To give a bit more color as to why I would like to know the parent of a deleted entity. In a use case I was testing, I noticed that entity ids are only unique in an entities list. I don’t know if this is a general invariant or just for what I was doing. I created a simple plane and subsequently created a component definition from the plane, replacing the initial plane with a component instance. The sequence of events I observed is as follows:

  • Create a new face with ID 1000. (via the EntitiesObserver::OnElementAdded() callback). This is the original plane.
  • Add a new component definition (via the DefinitionObserver::OncomponentDefintionAdded callback). This definition will also have a face with entityID 1000.
  • Delete face with ID 1000 (via the EntitiesObserver::OnElementRemoved callback).

So for a brief moment, there are 2 faces in the model, both with entityID 1000. Now without knowing the parent of entityID 1000, I don’t know where I need to remove the face from because it’s in 2 entity lists. Once in the model entity list, once in the entity list of the new component definition.

This was a problem in my code, which I have to work around now.


Sorry, I do not believe this. It probably only appears this way from the order of callbacks fired.

Perhaps you should read the PDF explaining the Observer overhaul that was done for the SketchUp 2016 release.
The "this PDF" link is in the API Release Notes:

Sorry, I do not believe this. It probably only appears this way from the order of callbacks fired.

Yes, you are probably right. But that’s the model’s state from my plugin’s perspective based on how the code observes the ordering of the callbacks.

Thanks for the PDF resource. That’s super useful!