Doc: Improve PropertyContainer documentation
This commit is contained in:
committed by
Chris Hennes
parent
9b9f8d1800
commit
5953fb6c24
@@ -73,13 +73,30 @@ enum PropertyType
|
||||
|
||||
struct AppExport PropertyData
|
||||
{
|
||||
|
||||
/// @brief Struct to hold the property specification.
|
||||
struct PropertySpec
|
||||
{
|
||||
/// The name of the property.
|
||||
const char * Name;
|
||||
/// The group name of the property.
|
||||
const char * Group;
|
||||
/// The documentation string of the property.
|
||||
const char * Docu;
|
||||
short Offset, Type;
|
||||
/// The offset of the property in the container.
|
||||
short Offset;
|
||||
/// The type of the property.
|
||||
short Type;
|
||||
|
||||
/**
|
||||
* @brief Construct a PropertySpec.
|
||||
*
|
||||
* @param[in] name The name of the property.
|
||||
* @param[in] group The group name of the property.
|
||||
* @param[in] doc The documentation string of the property.
|
||||
* @param[in] offset The offset of the property in the container.
|
||||
* @param[in] type The type of the property.
|
||||
*/
|
||||
inline PropertySpec(const char *name, const char *group, const char *doc, short offset, short type)
|
||||
:Name(name),Group(group),Docu(doc),Offset(offset),Type(type)
|
||||
{}
|
||||
@@ -89,15 +106,37 @@ struct AppExport PropertyData
|
||||
//be able to return the offset to a property from the accepted containers. This allows you to use
|
||||
//one function implementation for multiple container types without losing all type safety by
|
||||
//accepting void*
|
||||
/**
|
||||
* @brief Struct that represents the base for offset calculation.
|
||||
*
|
||||
* This struct is used to calculate the offset of a property in a container.
|
||||
* It can be constructed from either a PropertyContainer or an Extension.
|
||||
*/
|
||||
struct OffsetBase
|
||||
{
|
||||
/**
|
||||
* @brief Construct an OffsetBase from a PropertyContainer.
|
||||
*
|
||||
* @param[in] container The PropertyContainer to construct from.
|
||||
*/
|
||||
// Lint wants these marked explicit, but they are currently used implicitly in enough
|
||||
// places that I don't wnt to fix it. Instead we disable the Lint message.
|
||||
// NOLINTNEXTLINE(runtime/explicit)
|
||||
OffsetBase(const App::PropertyContainer* container) : m_container(container) {}
|
||||
/**
|
||||
* @brief Construct an OffsetBase from an Extension.
|
||||
*
|
||||
* @param[in] container The Extension to construct from.
|
||||
*/
|
||||
// NOLINTNEXTLINE(runtime/explicit)
|
||||
OffsetBase(const App::Extension* container) : m_container(container) {}
|
||||
|
||||
/**
|
||||
* @brief Get the offset to a property.
|
||||
*
|
||||
* @param[in] prop The property to get the offset to.
|
||||
* @return The offset to the property relative to the base offset.
|
||||
*/
|
||||
short int getOffsetTo(const App::Property* prop) const {
|
||||
auto *pt = (const char*)prop;
|
||||
auto *base = (const char *)m_container;
|
||||
@@ -105,6 +144,11 @@ struct AppExport PropertyData
|
||||
return -1;
|
||||
return (short) (pt-base);
|
||||
}
|
||||
/**
|
||||
* @brief Get the base offset in bytes.
|
||||
*
|
||||
* @return The base offset in bytes.
|
||||
*/
|
||||
char* getOffset() const {return (char*) m_container;}
|
||||
|
||||
private:
|
||||
@@ -112,11 +156,15 @@ struct AppExport PropertyData
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
// A multi index container for holding the property spec, with the following
|
||||
// index,
|
||||
// * a sequence, to preserve creation order
|
||||
// * hash index on property name
|
||||
// * hash index on property pointer offset
|
||||
|
||||
/**
|
||||
* @brief A multi index container for holding the property spec.
|
||||
*
|
||||
* The multi index has the following index:
|
||||
* - a sequence, to preserve creation order
|
||||
* - hash index on property name
|
||||
* - hash index on property pointer offset
|
||||
*/
|
||||
mutable bmi::multi_index_container<
|
||||
PropertySpec,
|
||||
bmi::indexed_by<
|
||||
@@ -133,15 +181,52 @@ struct AppExport PropertyData
|
||||
> propertyData;
|
||||
// clang-format on
|
||||
|
||||
/// Whether the property data is merged with the parent.
|
||||
mutable bool parentMerged = false;
|
||||
|
||||
/// The parent property data.
|
||||
const PropertyData* parentPropertyData;
|
||||
|
||||
void addProperty(OffsetBase offsetBase,const char* PropName, Property *Prop, const char* PropertyGroup= nullptr, PropertyType = Prop_None, const char* PropertyDocu= nullptr );
|
||||
/**
|
||||
* @brief Add a property to the property data.
|
||||
*
|
||||
* @param[in] offsetBase The base offset to offset the property.
|
||||
* @param[in] PropName The name of the property.
|
||||
* @param[in] Prop The property to add.
|
||||
* @param[in] PropertyGroup The group name of the property.
|
||||
* @param[in] Type The type of the property.
|
||||
* @param[in] PropertyDocu The documentation string of the property.
|
||||
*/
|
||||
void addProperty(OffsetBase offsetBase,const char* PropName, Property *Prop, const char* PropertyGroup= nullptr, PropertyType Type=Prop_None, const char* PropertyDocu=nullptr );
|
||||
|
||||
/**
|
||||
* @brief Find a property by its name.
|
||||
*
|
||||
* @param[in] offsetBase The base offset for the property.
|
||||
* @param[in] PropName The name of the property to find.
|
||||
* @return The property specification if found; `nullptr` otherwise.
|
||||
*/
|
||||
const PropertySpec *findProperty(OffsetBase offsetBase,const char* PropName) const;
|
||||
|
||||
/**
|
||||
* @brief Find a property by its pointer.
|
||||
*
|
||||
* @param[in] offsetBase The base offset for the property.
|
||||
* @param[in] prop The property to find.
|
||||
* @return The property specification if found; `nullptr` otherwise.
|
||||
*/
|
||||
const PropertySpec *findProperty(OffsetBase offsetBase,const Property* prop) const;
|
||||
|
||||
/**
|
||||
* @name PropertyData accessors
|
||||
*
|
||||
* @details These methods are used to access the property data based on:
|
||||
* - the offset base,
|
||||
* - either:
|
||||
* - a property pointer, or
|
||||
* - a property name.
|
||||
* @{
|
||||
*/
|
||||
const char* getName (OffsetBase offsetBase,const Property* prop) const;
|
||||
short getType (OffsetBase offsetBase,const Property* prop) const;
|
||||
short getType (OffsetBase offsetBase,const char* name) const;
|
||||
@@ -149,15 +234,70 @@ struct AppExport PropertyData
|
||||
const char* getGroup (OffsetBase offsetBase,const Property* prop) const;
|
||||
const char* getDocumentation(OffsetBase offsetBase,const char* name) const;
|
||||
const char* getDocumentation(OffsetBase offsetBase,const Property* prop) const;
|
||||
/// @}
|
||||
|
||||
/**
|
||||
* @brief Get a property by its name.
|
||||
*
|
||||
* @param[in] offsetBase The base offset for the property.
|
||||
* @param[in] name The name of the property to find.
|
||||
* @return The property if found; `nullptr` otherwise.
|
||||
*/
|
||||
Property *getPropertyByName(OffsetBase offsetBase,const char* name) const;
|
||||
|
||||
/**
|
||||
* @brief Get a map of properties.
|
||||
*
|
||||
* @param[in] offsetBase The base offset for the property.
|
||||
* @param[out] Map A map of property names to properties.
|
||||
*/
|
||||
void getPropertyMap(OffsetBase offsetBase,std::map<std::string,Property*> &Map) const;
|
||||
|
||||
/**
|
||||
* @brief Get a list of properties.
|
||||
*
|
||||
* @param[in] offsetBase The base offset for the property.
|
||||
* @param[out] List A vector of properties.
|
||||
*/
|
||||
void getPropertyList(OffsetBase offsetBase,std::vector<Property*> &List) const;
|
||||
|
||||
/**
|
||||
* @brief Get a list of properties with their names.
|
||||
*
|
||||
* @param[in] offsetBase The base offset for the property.
|
||||
* @param[out] List A vector of pairs, where each pair contains the name and
|
||||
* the property.
|
||||
*/
|
||||
void getPropertyNamedList(OffsetBase offsetBase, std::vector<std::pair<const char*,Property*> > &List) const;
|
||||
/// See PropertyContainer::visitProperties for semantics
|
||||
|
||||
/**
|
||||
* @brief Visit each property in the PropertyData struct.
|
||||
*
|
||||
* @param[in] offsetBase The base offset for the property.
|
||||
* @param[in] visitor The function to apply to each property.
|
||||
*
|
||||
* @see PropertyContainer::visitProperties()
|
||||
*/
|
||||
void visitProperties(OffsetBase offsetBase, const std::function<void(Property*)>& visitor) const;
|
||||
|
||||
/**
|
||||
* @brief Merge the PropertyData structs.
|
||||
*
|
||||
* Merge two PropertyData structs. If `other` is `nullptr`, merge with the
|
||||
* parent PropertyData.
|
||||
*
|
||||
* @param[in] other (Optional) The other PropertyData to merge with;
|
||||
*/
|
||||
void merge(PropertyData *other=nullptr) const;
|
||||
|
||||
/**
|
||||
* @brief Split the PropertyData structs.
|
||||
*
|
||||
* This method splits the PropertyData structs. It is used to
|
||||
* restore the parent PropertyData after a merge.
|
||||
*
|
||||
* @param[in] other The other PropertyData to split with; this can be the parent PropertyData.
|
||||
*/
|
||||
void split(PropertyData *other);
|
||||
};
|
||||
|
||||
@@ -251,7 +391,7 @@ public:
|
||||
*
|
||||
* The list may contain duplicates and aliases.
|
||||
*
|
||||
* @param[out] List A vector of pairs, where each pair contains the name and
|
||||
* @param[out] list A vector of pairs, where each pair contains the name and
|
||||
* the property.
|
||||
*/
|
||||
virtual void getPropertyNamedList(std::vector<std::pair<const char*,Property*> > &list) const;
|
||||
|
||||
@@ -62,17 +62,28 @@
|
||||
*
|
||||
* @section propframe_intro Introduction
|
||||
*
|
||||
* The property framework introduces the ability to access attributes (member
|
||||
* variables) of a class by name without knowing the class type. It's like the
|
||||
* reflection mechanism of Java or C#. This ability is introduced by the @ref
|
||||
* App::PropertyContainer "PropertyContainer" class and can be used by all
|
||||
* derived classes. In particular, there are two classes that inherit from
|
||||
* @ref App::PropertyContainer "PropertyContainer" which are @ref App::Document
|
||||
* The property framework introduces an intricate system to access properties
|
||||
* of objects. It provides the ability to:
|
||||
* 1. define properties in a class and access them by name,
|
||||
* 2. add properties to a class at runtime and access them by name, and
|
||||
* 3. access properties of a class by name without knowing the class type.
|
||||
*
|
||||
* The first two points are similar to what the dynamic reflection framework of
|
||||
* C# or Java offer. The third point allows FreeCAD to have App::Property as a
|
||||
* generic interface to access properties. This is similar to the way that
|
||||
* Python allows to access properties of a class by name.
|
||||
*
|
||||
* This ability is introduced by the @ref App::PropertyContainer
|
||||
* "PropertyContainer" class and can be used by all derived classes. In
|
||||
* particular, there are two classes that inherit from @ref
|
||||
* App::PropertyContainer "PropertyContainer" which are @ref App::Document
|
||||
* "Document" and @ref App::DocumentObject "DocumentObject". These two classes
|
||||
* serve different purposes but are both able to hold properties. @ref
|
||||
* App::PropertyContainer "PropertyContainer" contains the shared logic to do
|
||||
* so.
|
||||
*
|
||||
* @section propframe_static_dynamic_props Static/Dynamic Properties
|
||||
*
|
||||
* In C++, it is possible to define properties as part of the class. These can
|
||||
* be considered "static" properties but this term is typically not used within
|
||||
* FreeCAD. Properties created in a class cannot be removed from a @ref
|
||||
@@ -81,13 +92,65 @@
|
||||
* However, it is also possible to add properties to a @ref
|
||||
* App::PropertyContainer "PropertyContainer" at runtime. These properties are
|
||||
* called "dynamic properties" and these properties can be freely added and
|
||||
* removed by users.
|
||||
* removed by users. In Python, all properties are dynamic properties.
|
||||
*
|
||||
* In Python, all properties are dynamic properties. This makes it difficult
|
||||
* to understand whether properties are part of a Python class and are
|
||||
* necessary for the functioning of the class, or whether a user has added
|
||||
* these properties. Therefore, it is possible to indicate that a property is
|
||||
* "locked":
|
||||
* @section propframe_mechanisms Mechanisms
|
||||
*
|
||||
* The macros `PROPERTY_HEADER` and `PROPERTY_SOURCE` (and variants) are used
|
||||
* to define a private static PropertyData member in the class. This data
|
||||
* structure contains a multi index that maps 1) the property name to the
|
||||
* property specifications @ref App::PropertyData::PropertySpec "PropertySpec",
|
||||
* allowing access of properties by name, and 2) maps the offset of a property
|
||||
* with respect to its container, allowing access to property specifications.
|
||||
*
|
||||
* The static function @ref App::PropertyContainer::getPropertyDataPtr()
|
||||
* "PropertyContainer::getPropertyDataPtr()" is used to access the class-wide
|
||||
* static @ref App::PropertyData "PropertyData" structure shared by all
|
||||
* instances of the class. The virtual function @ref
|
||||
* App::PropertyContainer::getPropertyData()
|
||||
* "PropertyContainer::getPropertyData()" allows access to the class-level
|
||||
* static PropertyData based on the dynamic type of the object, even when
|
||||
* downcasted. This allows for example a @ref App::PropertyInteger
|
||||
* "PropertyInteger*" downcasted to @ref App::Property "Property*" to access
|
||||
* all its properties.
|
||||
*
|
||||
* Since the @ref App::PropertyData "PropertyData" structure is static in the
|
||||
* class and maintains static information of properties, such as the group and
|
||||
* documentation, we need to be able to access a specific instance given a
|
||||
* property name. This is achieved by looking up the @ref
|
||||
* App::PropertyData::PropertySpec "PropertySpec" in the index based on the
|
||||
* property name. The property specification stores an offset to the property
|
||||
* address given an @ref App::PropertyData::OffsetBase "OffsetBase" which wraps
|
||||
* the address of a @ref App::PropertyContainer "PropertyContainer". See @ref
|
||||
* App::PropertyData::findProperty() "PropertyData::findProperty()" and @ref
|
||||
* App::PropertyData::OffsetBase::getOffsetTo() "OffsetBase::getOffsetTo()".
|
||||
* The offset of the property relative to the offset base gives us the address
|
||||
* of the property in the instance. Note that different values for properties
|
||||
* across property containers are stored in the @ref App::Property "Property" instances.
|
||||
*
|
||||
* The reverse is also needed: Given a property in a property container, it is
|
||||
* possible to compute the offset relative to the base of the property
|
||||
* container. The index in @ref App::PropertyData "PropertyData" allows us to
|
||||
* acquire the property spec and provides us with the static data associated
|
||||
* with the property.
|
||||
*
|
||||
* Dynamic properties are stored in their own @ref App::DynamicProperty
|
||||
* "DynamicProperty" container. It can be added with the function @ref
|
||||
* App::PropertyContainer::addDynamicProperty()
|
||||
* "PropertyContainer::addDynamicProperty()" and removed with @ref
|
||||
* App::PropertyContainer::removeDynamicProperty()
|
||||
* "PropertyContainer::removeDynamicProperty()". The rest of the interface of
|
||||
* dynamic properties is virtually the same. In general, in FreeCAD, it is
|
||||
* difficult for users to know whether a property is dynamic or static and they
|
||||
* typically do not need to be concerned with this distinction.
|
||||
*
|
||||
* @section Locking Dynamic Properties
|
||||
*
|
||||
* Since in Python all properties are dynamic properties, it is difficult to
|
||||
* understand whether properties are part of a Python class and are necessary
|
||||
* for the functioning of the class, or whether a user has added these
|
||||
* properties. Therefore, it is possible to indicate that a property is
|
||||
* "locked":
|
||||
*
|
||||
* @code
|
||||
* prop->setStatus(Property::LockDynamic, true);
|
||||
@@ -107,13 +170,12 @@
|
||||
*
|
||||
* @section Examples
|
||||
*
|
||||
* Here some little examples how to use it:
|
||||
* Here some little examples how to use the property framework:
|
||||
*
|
||||
* @code
|
||||
* // search in PropertyList
|
||||
* Property *prop = _pcFeature->getPropertyByName(attr);
|
||||
* if(prop)
|
||||
* {
|
||||
* // Acquire a property by name and return the value as a Python object.
|
||||
* Property *prop = feature->getPropertyByName(nameProperty);
|
||||
* if (prop) {
|
||||
* return prop->getPyObject();
|
||||
* }
|
||||
* @endcode
|
||||
@@ -121,18 +183,19 @@
|
||||
* or:
|
||||
*
|
||||
* @code
|
||||
* // Restore properties from a reader.
|
||||
* void PropertyContainer::Restore(Base::Reader &reader)
|
||||
* {
|
||||
* reader.readElement("Properties");
|
||||
* int Cnt = reader.getAttributeAsInteger("Count");
|
||||
*
|
||||
* for(int i=0 ;i<Cnt ;i++)
|
||||
* {
|
||||
* for(int i=0 ;i<Cnt ;i++) {
|
||||
* reader.readElement("Property");
|
||||
* string PropName = reader.getAttribute("name");
|
||||
* Property* prop = getPropertyByName(PropName.c_str());
|
||||
* if(prop)
|
||||
* if(prop) {
|
||||
* prop->Restore(reader);
|
||||
* }
|
||||
*
|
||||
* reader.readEndElement("Property");
|
||||
* }
|
||||
|
||||
Reference in New Issue
Block a user