// // SPDX-License-Identifier: BSD-3-Clause // Copyright Contributors to the OpenEXR Project. // // // This file contains type traits related to or used by the Imath library. // #ifndef INCLUDED_IMATHTYPETRAITS_H #define INCLUDED_IMATHTYPETRAITS_H #include #include "ImathPlatform.h" IMATH_INTERNAL_NAMESPACE_HEADER_ENTER /// Define Imath::enable_if_t to be std for C++14, equivalent for C++11. #if (IMATH_CPLUSPLUS_VERSION >= 14) using std::enable_if_t; // Use C++14 std::enable_if_t #else // Define enable_if_t for C++11 template using enable_if_t = typename std::enable_if::type; #endif /// An enable_if helper to be used in template parameters which results in /// much shorter symbols. #define IMATH_ENABLE_IF(...) IMATH_INTERNAL_NAMESPACE::enable_if_t<(__VA_ARGS__), int> = 0 #if IMATH_FOREIGN_VECTOR_INTEROP /// @{ /// @name Detecting interoperable types. /// /// In order to construct or assign from external "compatible" types without /// prior knowledge of their definitions, we have a few helper type traits. /// The intent of these is to allow custom linear algebra types in an /// application that have seamless conversion to and from Imath types. /// /// `has_xy`, `has_xyz`, `has_xyzw` detect if class /// `T` has elements `.x`, `.y`, and `.z` all of type `Base` and seems to be /// the right size to hold exactly those members and nothing more. /// /// `has_subscript` detects if class `T` can perform `T[int]` /// to yield a `Base`, and that it seems to be exactly the right size to /// hold `N` of those elements. /// /// This is not exact. It's possible that for a particular user-defined /// type, this may yield a false negative or false positive. For example: /// * A class for a 3-vector that contains an extra element of padding /// so that it will have the right size and alignment to use 4-wide /// SIMD math ops will appear to be the wrong size. /// * A `std::vector` is subscriptable and might have N elements at /// runtime, but the size is dynamic and so would fail this test. /// * A foreign type may have .x, .y, .z that are not matching our base /// type but we still want it to work (with appropriate conversions). /// /// In these cases, user code may declare an exception -- for example, /// stating that `mytype` should be considered implicitly convertible to /// an Imath::V3f by subscripting: /// /// template<> /// struct Imath::has_subscript : public std::true_type { }; /// /// And similarly, user code may correct a potential false positive (that /// is, a `mytype` looks like it should be convertible to a V3f, but you /// don't want it to ever happen): /// /// template /// struct Imath::has_subscript : public std::false_type { }; /// /// `has_xy::value` will be true if type `T` has member variables /// `.x` and `.y`, all of type `Base`, and the size of a `T` is exactly big /// enough to hold 2 Base values. template struct has_xy { private: typedef char Yes[1]; typedef char No[2]; // Valid only if .x, .y exist and are the right type: return a Yes. template < typename C, IMATH_ENABLE_IF (std::is_same::value), IMATH_ENABLE_IF (std::is_same::value)> static Yes& test (int); // Fallback, default to returning a No. template static No& test(...); public: enum { value = (sizeof(test(0)) == sizeof(Yes) && sizeof(T) == 2*sizeof(Base)) }; }; /// `has_xyz::value` will be true if type `T` has member variables /// `.x`, `.y`, and `.z`, all of type `Base`, and the size of a `T` is /// exactly big enough to hold 3 Base values. template struct has_xyz { private: typedef char Yes[1]; typedef char No[2]; // Valid only if .x, .y, .z exist and are the right type: return a Yes. template < typename C, IMATH_ENABLE_IF (std::is_same::value), IMATH_ENABLE_IF (std::is_same::value), IMATH_ENABLE_IF (std::is_same::value)> static Yes& test (int); // Fallback, default to returning a No. template static No& test(...); public: enum { value = (sizeof(test(0)) == sizeof(Yes) && sizeof(T) == 3*sizeof(Base)) }; }; /// `has_xyzw::value` will be true if type `T` has member variables /// `.x`, `.y`, `.z`, and `.w`, all of type `Base`, and the size of a `T` is /// exactly big enough to hold 4 Base values. template struct has_xyzw { private: typedef char Yes[1]; typedef char No[2]; // Valid only if .x, .y, .z, .w exist and are the right type: return a Yes. template < typename C, IMATH_ENABLE_IF (std::is_same::value), IMATH_ENABLE_IF (std::is_same::value), IMATH_ENABLE_IF (std::is_same::value), IMATH_ENABLE_IF (std::is_same::value)> static Yes& test (int); // Fallback, default to returning a No. template static No& test(...); public: enum { value = (sizeof(test(0)) == sizeof(Yes) && sizeof(T) == 4*sizeof(Base)) }; }; /// `has_subscript::value` will be true if type `T` has /// subscripting syntax, a `T[int]` returns a `Base`, and the size of a `T` /// is exactly big enough to hold `Nelem` `Base` values. template struct has_subscript { private: typedef char Yes[1]; typedef char No[2]; // Valid only if T[] is possible and is the right type: return a Yes. template < typename C, IMATH_ENABLE_IF (std::is_same< typename std::decay::type, Base>::value)> static Yes& test (int); // Fallback, default to returning a No. template static No& test(...); public: enum { value = (sizeof(test(0)) == sizeof(Yes) && sizeof(T) == Nelem*sizeof(Base)) }; }; /// C arrays of just the right length also are qualified for has_subscript. template struct has_subscript : public std::true_type { }; /// `has_double_subscript::value` will be true if type `T` /// has 2-level subscripting syntax, a `T[int][int]` returns a `Base`, and /// the size of a `T` is exactly big enough to hold `R*C` `Base` values. template struct has_double_subscript { private: typedef char Yes[1]; typedef char No[2]; // Valid only if T[][] is possible and is the right type: return a Yes. template < typename C, IMATH_ENABLE_IF (std::is_same< typename std::decay::type, Base>::value)> static Yes& test (int); // Fallback, default to returning a No. template static No& test(...); public: enum { value = (sizeof(test(0)) == sizeof(Yes) && sizeof(T) == (Rows*Cols)*sizeof(Base)) }; }; /// C arrays of just the right length also are qualified for has_double_subscript. template struct has_double_subscript : public std::true_type { }; /// @} #endif IMATH_INTERNAL_NAMESPACE_HEADER_EXIT #endif // INCLUDED_IMATHTYPETRAITS_H