bbm::attribute
BBM attributes (not to be confused with class attributes) are a wrapper for any type that allows the programmer to attach additional compile time information. BBM uses attributes in two ways:
to attach a tag to a type to differentiate between different interpretations of the value (cf. a unit like ‘ft’ or ‘cm’). BBM uses this to differentiate between index of refraction and reflectance at normal incidence. Both are used to parameterize the Fresnel reflectance equations.
to attach default value, bounds, and type to an BSDF parameter, implemented through
bbm::bsdf_parameter.
bbm::attribute is implemented in core/attribute.h and meets
bbm::concepts::attribute:
-
template<typename T>
concept attribute - #include <attribute.h>
attribute concept
Each attribute provides the following:
typename type : the underlying type
typename prop : requires attribute_property
convertible_to the underlying (reference) type
auto value() : return the cast to a reference of the underlying type
auto value() const : return the cast to a const reference of the underlying type
There are two specializations of bbm::attribute: one for scalar types and
one for non-scalar types. Both operate similarly with the key difference that
bbm::atrribute stores a scalar type as a class member and it inherits from
the non-scalar type (in order to inherit its functions).
bbm::attribute takes one template parameter that must meet
bbm::concepts::attribute_property which describes the type and any other
optional information one wants to attach to the type:
-
template<typename PROP>
concept attribute_property - #include <attribute.h>
attribute_property concept
Each attribute_property must at least contain a typedef named ‘type’ that indicates the underlying attribute type.
The bbm::attribute implementation adds a few extra features besides the
requirements set in bbm::concepts::attribute:
If constructed from a type that meets
concepts::constructible, then a customconvertmethod is called to convert the value of the construction type to the attribute type. This is for example used to correctly covert from index of refraction to reflectance and vice versa.Math operations are defined to automatically forward to the underlying type.
bbm::attributeforwards any valid cast operation to the underlying type.
bbm::attribute is defined to be transparent by automatically casting to
the underlying type when required. However, this does not (annoyingly)
always work. For example in the following case:
template<typename T> requires std::is_scalar_v<T>
void foo(const T& t) {}
void bar(int t) {}
struct prop { using type = int; };
bbm::attribute<prop> scalar;
bar(scalar); // OK
foo(scalar); // compile error
In the case of ‘’bar’’ the attribute scalar is automatically cast to an
int. However, in the case of foo, the compiler does not know which
type what type to cast scalar to; C++ lacks the ability to examine
the intersection of types that meet the constraints and types scalar can
be cast to.
bbm::value
In certain cases automatic casting of attributes to their underlying type does not work in the following case:
template<typename T> requires std::is_integral<T>
void foo(const T& t);
Even if the underlying type is integral, C++ does not know that it should first cast the attribute to its type. To alleviate this, BBM includes (defined in util/attribute_value.h:
-
template<typename T>
decltype(auto) bbm::value(T &&t) return the value of an attribute, or if not an attribute the object
This method will cast the attribute to its value type (or leave it unaltered if not an attribute). Similarly, sometimes the type of the underlying type is needed, but it is not always the case that the type is an attribute:
-
template<typename T>
using bbm::attribute_value_t = bbm::detail::attribute_value<std::decay_t<T>>::type return the type of value(t)
will either return the type of T or, if T is an attribute, typename T::type.