Back when the attribute resource was being designed (when it was called an Attribute System), I needed to be able to refer to model and mesh entities and attributes as items within an attribute. Since there was no base class that the 3 classes shared, I created specific items classes that became ModelEntityItem, MeshEntityItem, and AttrRefItem respectively along with their corresponding Definition classes.
In SMTK 3.0, we have the resource::Component class that is the base class to attribute::Attribute, model::Component, and mesh::Component. There is now an attribute::ComponentItem class (along with its corresponding Definition class) that can functionally do all the things that the original 3 item classes can do. The question is do we remove the other classes or keep them around as convenience classes that target specifically these less abstract concepts and potentially provide additional API? For example, ModelEntityItem has the ability to use EntityRefs while the superclass would not.
We would like to make a decision soon but wanted to give other developers a chance to weigh in. So comments are strongly encouraged!
I think the base differentiation is whether we want to put the pointer downcasts in a narrowing API within attribute or have the downcast be performed by the consuming code. One of the primary places we encourage smtk users to extend smtk’s functionality is the ability to add Operations, and most of the downcasts would have to be performed within the operation code (i.e. smtk users will have to perform a bunch of downcasts when writing an operation). A narrowing API would lift this burden from writers of operations, but would augment the API of smtk::attribute, and thus provide another cognitive load for users. I’m not sure which one is better, but I think contributors may be more comfortable with a larger API than with the need to cast everything into its expected type.
The need to cast everything also leaks our implementation of component hierarchy through attribute’s API. While we are not currently trying to hide the fact that components exist, it is definitely an implementation and not a front-facing feature of smtk.
Actually, the ComponentItem does allow EntityRefs because EntityRef can take an Entity::Ptr for a constructor. This means that the ReferenceItem::as<std::vector>() will still work through the beauty of templates.
The problem is that the pointer downcasts occur frequently and involve a lot of boilerplate code that is painful if you do not use SMTK every day.
I prefer to just use the templated as<Container>() method in ReferenceItem than adding a whole narrowing class. But the worst of both worlds is to provide neither.
What value do narrowing classes provide beyond that one function? Is it Python access to items? I would prefer to provide a python-specific list conversion for that.
auto item = this->parameters()->findComponent("foo"); auto faces = item->as<smtk::model::Faces>([](smtk::resource::PersistentObjectPtr obj) { return smtk::model::Face(std::dynamic_pointer_cast<smtk::model::Entity>(obj)); });
I could definitely be doing this in a silly way, but this is a pretty awkward API. In contrast, there would be something like
auto item = this->parameters()->findModelEntity("foo"); smtk::model::Faces faces(item->begin(), item->end());
If smtk::model::EntityRef were going away (which seems like a pretty huge undertaking), then I agree that a narrowing API to avoid std::dynamic_pointer_cast<> (or as<>) would probably be a long trip for a short slide.
So maybe the question is whether or not we want to force the public-facing API of each of the core namespaces (attribute, mesh, model) to only use the Resource/Component paradigm. The attribute system was virtually a no-op, and I know the model part is tricky. The mesh part is going to be even trickier (not impossible, though. It’s just code).
One thing to consider: if we have specialized items, then we will also introduce a dependency from the namespace defining the API back to the attribute namespace. In the past we have discussed the possibility of allowing libraries to register items and definitions with the attribute resource; doing so would remove this dependency.