I had a bug recently where a function had multiple bool parameters and their order in the declaration, implementation, and call did not all mutually match, leading to hilarious/tragic results.
I've been toying with the use of strong types to help curb this kind of error. I'm not yet committed to this, but am curious what people think, both of the technique from a programming standpoint, and also whether we should use this idiom in places where it would bring clarity. It's best to illustrate with an example, below. Feedback appreciated. ---- #include <iostream> #include <type_traits> // Inspired by https://lists.llvm.org/pipermail/llvm-dev/2019-August/134302.html // Implementation of a derived type that lets you pass strongly typed // parameters. It implicitly converts TO the basetype, but requires // explicit conversion FROM the basetype. // template<typename Tag, typename Basetype> struct StrongParam { public: explicit StrongParam(const Basetype& val) : m_val(val) {} StrongParam(const StrongParam<Tag, Basetype>& val) = default; operator const Basetype&() const noexcept { return m_val; } private: Basetype m_val; static_assert(std::is_trivial<Basetype>::value, "Need trivial type"); }; // Convenience macro for making a parameter type Name that is Basetype underneath. // #define STRONG_PARAM_TYPE(Name,Basetype) \ struct Name : public StrongParam<Name, Basetype> { \ using StrongParam::StrongParam; \ } // Example 1: Use StrongParam to disambiguate two different bools. // Error prone: compute(bool,bool) // Idiot proof: compute(Verbose,Crazy) STRONG_PARAM_TYPE(Verbose, bool); STRONG_PARAM_TYPE(Crazy, bool); bool compute (Verbose a, Crazy b) { return a | b; } // Example 2: Use StrongParam to disambiguate two floats. // Error prone: speed(a,b) // Idiot proof: speed(Meters,Seconds) STRONG_PARAM_TYPE(Meters, float); STRONG_PARAM_TYPE(Seconds, float); float speed (Meters a, Seconds b) { return a / b; } int main (int argc, char *argv[]) { // std::cout << compute(true, true) << '\n'; // Fails! std::cout << compute(Verbose(true), Crazy(true)) << "\n"; // Works! // std::cout << compute(Crazy(true), Verbose(true)) << "\n"; // Fails! // std::cout << speed(10.0f, 5.0f) << "\n"; // Fails! std::cout << speed(Meters(10.0f), Seconds(5.0f)) << "\n"; // Works! // std::cout << speed(Seconds(10.0f), Meters(5.0f)) << "\n"; // Fails! } -- Larry Gritz l...@larrygritz.com _______________________________________________ Oiio-dev mailing list Oiio-dev@lists.openimageio.org http://lists.openimageio.org/listinfo.cgi/oiio-dev-openimageio.org