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 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_results
andwrap_inputs
/wrap_results
functions can be used at runtime to go from/to type-erased inputs/results.
Domain agnostic
CRTP allows the
PropertyType
class 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
FieldTuple
component.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
PropertyType
is templated on base property types.