+ template <bool cond, typename T>
+ using enableIf = typename std::enable_if<cond, T>::type;
+ // std::enable_if_t does not appear to be present in GCC????
+ // std::is_enum_v also missing. wtf. these are supposed to be
+ // present as of c++14
+
+ template<typename T> using BlendFunction = T(float,T,T);
+ #define DECL_PARAM_SRZRS(type) \
+ void serializeParameterValue (std::ostream& os, type v); \
+ void deSerializeParameterValue(std::istream& is, type& r);
+ #define DECL_PARAM_OVERLOADS(type) DECL_PARAM_SRZRS(type) \
+ type interpolateParameterValue(float fac, const type a, const type b); \
+ type pickParameterValue (float* facs, const type a, const type b);
+
+ DECL_PARAM_OVERLOADS(u8); DECL_PARAM_OVERLOADS(s8);
+ DECL_PARAM_OVERLOADS(u16); DECL_PARAM_OVERLOADS(s16);
+ DECL_PARAM_OVERLOADS(u32); DECL_PARAM_OVERLOADS(s32);
+ DECL_PARAM_OVERLOADS(f32);
+ DECL_PARAM_OVERLOADS(v2f);
+ DECL_PARAM_OVERLOADS(v3f);
+
+ /* C++ is a strongly typed language. this means that enums cannot be implicitly
+ * cast to integers, as they can be in C. while this may sound good in principle,
+ * it means that our normal serialization functions cannot be called on
+ * enumerations unless they are explicitly cast to a particular type first. this
+ * is problematic, because in C++ enums can have any integral type as an underlying
+ * type, and that type would need to be named everywhere an enumeration is
+ * de/serialized.
+ *
+ * this is obviously not cool, both in terms of writing legible, succinct code,
+ * and in terms of robustness: the underlying type might be changed at some point,
+ * e.g. if a bitmask gets too big for its britches. we could use an equivalent of
+ * `std::to_underlying(value)` everywhere we need to deal with enumerations, but
+ * that's hideous and unintuitive. instead, we supply the following functions to
+ * transparently map enumeration types to their underlying values. */
+
+ template <typename E, enableIf<std::is_enum<E>::value, bool> = true>
+ void serializeParameterValue(std::ostream& os, E k) {
+ serializeParameterValue(os, (std::underlying_type_t<E>)k);
+ }
+
+ template <typename E, enableIf<std::is_enum<E>::value, bool> = true>
+ void deSerializeParameterValue(std::istream& is, E& k) {
+ std::underlying_type_t<E> v;
+ deSerializeParameterValue(is, v);
+ k = (E)v;
+ }
+
+ /* this is your brain on C++. */
+
+ template <typename T, size_t PN>
+ struct Parameter
+ {
+ using ValType = T;
+ using pickFactors = float[PN];
+
+ T val = T();
+ using This = Parameter<T, PN>;
+
+ Parameter() = default;
+
+ template <typename... Args>
+ Parameter(Args... args) : val(args...) {}
+
+ virtual void serialize(std::ostream &os) const
+ { serializeParameterValue (os, this->val); }
+ virtual void deSerialize(std::istream &is)
+ { deSerializeParameterValue(is, this->val); }
+
+ virtual T interpolate(float fac, const This& against) const
+ {
+ return interpolateParameterValue(fac, this->val, against.val);
+ }
+
+ static T pick(float* f, const This& a, const This& b)
+ {
+ return pickParameterValue(f, a.val, b.val);
+ }
+
+ operator T() const { return val; }
+ T operator=(T b) { return val = b; }
+
+ };
+
+ template <typename T> T numericalBlend(float fac, T min, T max)
+ { return min + ((max - min) * fac); }
+
+ template <typename T, size_t N>
+ struct VectorParameter : public Parameter<T,N> {
+ using This = VectorParameter<T,N>;
+ template <typename... Args>
+ VectorParameter(Args... args) : Parameter<T,N>(args...) {}
+ };
+
+ template <typename T, size_t PN>
+ inline std::string dump(const Parameter<T,PN>& p)