Designing the Property Type Component
Designing the Call Graph Component calls for a property type component. This section outlines the design decisions that went into that component.
What is the Property Type Component?
At the lowest level all modules have the same API: take a series of type-erased inputs and return a set of type-erased outputs. In strongly typed languages like C++, type-erased objects are cumbersome to work with. The property type component facilitates the wrapping/unwrapping of typed/type-erased objects while also enforcing a standardized API.
Property Type Considerations
From Designing the Call Graph Component we have:
Dynamic determine module API
Property types will need to wrap/unwrap typed/type-erased data
Domain agnostic
PluginPlay needs to avoid coupling to the domain-specific types
While not explicitly called for at this point we also want:
Avoid exposing templates to the user (to the extent possible)
Property types will inevitably use templates, but many C++ programmers in computational science are not comfortable with advanced template usage.
Use property types to factor out input/result provenance
The same property type will be used by multiple modules. Rather than duplicating provenance, particularly input/result descriptions, in each module, we want to factor this out to the property type.
Allow property type inheritance
Many properties are related via “is-a-type-of” relationships.
Inheritance captures “is-a-type-of” relationships and can be used for avoiding duplication
Property Type Component Design
Fig. 8 Architecture of the property type component. Users derive from the
PropertyType class to implement their property types. The template
meta-programming needed for dealing with types is factored out into the
FieldTuple class.
Fig Fig. 8 shows the architecture of the property
type component. The PropertyType class holds the bulk of the implementation.
Users derive their property types from PropertyType, using the curiously-
recursive template pattern (CRTP). CRTP facilitates PluginPlay implementing
features on behalf of the user, without PluginPlay knowing the types. N.B.,
normal inheritance would not allow the PropertyType class to access the
types defined in the derived class. In our implementation PropertyType is
templated on the derived type and the base type(s). The PropertyType class
will implement four functions wrap_inputs, wrap_results,
unwrap_inputs, and unwrap_results which can be used to type-erase and
un-type-erase data on behalf of the user.
In the derived class, users fill in two FieldTuple objects, one for the
inputs and one for the results. The process of doing this is wrapped by
“virtual” functions (since we’re using CRTP they’re not actually virtual)
inputs_ and results_ respectively. The FieldTuple objects are
responsible for storing not only the types of the inputs, but also the default
values, descriptions, etc. It is the responsibility of the FieldTuple
objects to define as simple of an API as possible.
Summary
Our design addresses the above considerations by:
Dynamic determine module API
unwrap_inputs/unwrap_resultsandwrap_inputs/wrap_resultsfunctions can be used at runtime to go from/to type-erased inputs/results.
Domain agnostic
CRTP allows the
PropertyTypeclass to access derived class’s types through “inheritance”.Derived classes, and their types, live in downstream code.
Avoid exposing templates to the user (to the extent possible)
Largely falls to
FieldTuplecomponent.Macros further de-template the API.
Use property types to factor out input/result provenance
FieldTuple`stores provenance for inputs/results.Can be overridden on a per-module basis.
Allow property type inheritance
PropertyTypeis templated on base property types.