1//
2// SPDX-License-Identifier: BSD-3-Clause
3// Copyright Contributors to the OpenEXR Project.
4//
5
6//
7// This file contains type traits related to or used by the Imath library.
8//
9
10#ifndef INCLUDED_IMATHTYPETRAITS_H
11#define INCLUDED_IMATHTYPETRAITS_H
12
13#include <type_traits>
14
15#include "ImathPlatform.h"
16
17IMATH_INTERNAL_NAMESPACE_HEADER_ENTER
18
19
20/// Define Imath::enable_if_t to be std for C++14, equivalent for C++11.
21#if (IMATH_CPLUSPLUS_VERSION >= 14)
22 using std::enable_if_t; // Use C++14 std::enable_if_t
23#else
24 // Define enable_if_t for C++11
25 template <bool B, class T = void>
26 using enable_if_t = typename std::enable_if<B, T>::type;
27#endif
28
29
30/// An enable_if helper to be used in template parameters which results in
31/// much shorter symbols.
32#define IMATH_ENABLE_IF(...) IMATH_INTERNAL_NAMESPACE::enable_if_t<(__VA_ARGS__), int> = 0
33
34
35#if IMATH_FOREIGN_VECTOR_INTEROP
36
37/// @{
38/// @name Detecting interoperable types.
39///
40/// In order to construct or assign from external "compatible" types without
41/// prior knowledge of their definitions, we have a few helper type traits.
42/// The intent of these is to allow custom linear algebra types in an
43/// application that have seamless conversion to and from Imath types.
44///
45/// `has_xy<T,Base>`, `has_xyz<T,Base>`, `has_xyzw<T,Base>` detect if class
46/// `T` has elements `.x`, `.y`, and `.z` all of type `Base` and seems to be
47/// the right size to hold exactly those members and nothing more.
48///
49/// `has_subscript<T,Base,N>` detects if class `T` can perform `T[int]`
50/// to yield a `Base`, and that it seems to be exactly the right size to
51/// hold `N` of those elements.
52///
53/// This is not exact. It's possible that for a particular user-defined
54/// type, this may yield a false negative or false positive. For example:
55/// * A class for a 3-vector that contains an extra element of padding
56/// so that it will have the right size and alignment to use 4-wide
57/// SIMD math ops will appear to be the wrong size.
58/// * A `std::vector<T>` is subscriptable and might have N elements at
59/// runtime, but the size is dynamic and so would fail this test.
60/// * A foreign type may have .x, .y, .z that are not matching our base
61/// type but we still want it to work (with appropriate conversions).
62///
63/// In these cases, user code may declare an exception -- for example,
64/// stating that `mytype` should be considered implicitly convertible to
65/// an Imath::V3f by subscripting:
66///
67/// template<>
68/// struct Imath::has_subscript<mytype, float, 3> : public std::true_type { };
69///
70/// And similarly, user code may correct a potential false positive (that
71/// is, a `mytype` looks like it should be convertible to a V3f, but you
72/// don't want it to ever happen):
73///
74/// template<typename B, int N>
75/// struct Imath::has_subscript<mytype, B, N> : public std::false_type { };
76///
77
78
79/// `has_xy<T,Base>::value` will be true if type `T` has member variables
80/// `.x` and `.y`, all of type `Base`, and the size of a `T` is exactly big
81/// enough to hold 2 Base values.
82template <typename T, typename Base>
83struct has_xy {
84private:
85 typedef char Yes[1];
86 typedef char No[2];
87
88 // Valid only if .x, .y exist and are the right type: return a Yes.
89 template<typename C,
90 IMATH_ENABLE_IF(std::is_same<decltype(C().x), Base>::value),
91 IMATH_ENABLE_IF(std::is_same<decltype(C().y), Base>::value)>
92 static Yes& test(int);
93
94 // Fallback, default to returning a No.
95 template<typename C> static No& test(...);
96public:
97 enum { value = (sizeof(test<T>(0)) == sizeof(Yes)
98 && sizeof(T) == 2*sizeof(Base))
99 };
100};
101
102
103/// `has_xyz<T,Base>::value` will be true if type `T` has member variables
104/// `.x`, `.y`, and `.z`, all of type `Base`, and the size of a `T` is
105/// exactly big enough to hold 3 Base values.
106template <typename T, typename Base>
107struct has_xyz {
108private:
109 typedef char Yes[1];
110 typedef char No[2];
111
112 // Valid only if .x, .y, .z exist and are the right type: return a Yes.
113 template<typename C,
114 IMATH_ENABLE_IF(std::is_same<decltype(C().x), Base>::value),
115 IMATH_ENABLE_IF(std::is_same<decltype(C().y), Base>::value),
116 IMATH_ENABLE_IF(std::is_same<decltype(C().z), Base>::value)>
117 static Yes& test(int);
118
119 // Fallback, default to returning a No.
120 template<typename C> static No& test(...);
121public:
122 enum { value = (sizeof(test<T>(0)) == sizeof(Yes)
123 && sizeof(T) == 3*sizeof(Base))
124 };
125};
126
127
128/// `has_xyzw<T,Base>::value` will be true if type `T` has member variables
129/// `.x`, `.y`, `.z`, and `.w`, all of type `Base`, and the size of a `T` is
130/// exactly big enough to hold 4 Base values.
131template <typename T, typename Base>
132struct has_xyzw {
133private:
134 typedef char Yes[1];
135 typedef char No[2];
136
137 // Valid only if .x, .y, .z, .w exist and are the right type: return a Yes.
138 template<typename C,
139 IMATH_ENABLE_IF(std::is_same<decltype(C().x), Base>::value),
140 IMATH_ENABLE_IF(std::is_same<decltype(C().y), Base>::value),
141 IMATH_ENABLE_IF(std::is_same<decltype(C().z), Base>::value),
142 IMATH_ENABLE_IF(std::is_same<decltype(C().w), Base>::value)>
143 static Yes& test(int);
144
145 // Fallback, default to returning a No.
146 template<typename C> static No& test(...);
147public:
148 enum { value = (sizeof(test<T>(0)) == sizeof(Yes)
149 && sizeof(T) == 4*sizeof(Base))
150 };
151};
152
153
154
155/// `has_subscript<T,Base,Nelem>::value` will be true if type `T` has
156/// subscripting syntax, a `T[int]` returns a `Base`, and the size of a `T`
157/// is exactly big enough to hold `Nelem` `Base` values.
158template <typename T, typename Base, int Nelem>
159struct has_subscript {
160private:
161 typedef char Yes[1];
162 typedef char No[2];
163
164 // Valid only if T[] is possible and is the right type: return a Yes.
165 template<typename C,
166 IMATH_ENABLE_IF(std::is_same<typename std::decay<decltype(C()[0])>::type, Base>::value)>
167 static Yes& test(int);
168
169 // Fallback, default to returning a No.
170 template<typename C> static No& test(...);
171public:
172 enum { value = (sizeof(test<T>(0)) == sizeof(Yes)
173 && sizeof(T) == Nelem*sizeof(Base))
174 };
175};
176
177
178/// C arrays of just the right length also are qualified for has_subscript.
179template<typename Base, int Nelem>
180struct has_subscript<Base[Nelem], Base, Nelem> : public std::true_type { };
181
182
183
184/// `has_double_subscript<T,Base,Rows,Cols>::value` will be true if type `T`
185/// has 2-level subscripting syntax, a `T[int][int]` returns a `Base`, and
186/// the size of a `T` is exactly big enough to hold `R*C` `Base` values.
187template <typename T, typename Base, int Rows, int Cols>
188struct has_double_subscript {
189private:
190 typedef char Yes[1];
191 typedef char No[2];
192
193 // Valid only if T[][] is possible and is the right type: return a Yes.
194 template<typename C,
195 IMATH_ENABLE_IF(std::is_same<typename std::decay<decltype(C()[0][0])>::type, Base>::value)>
196 static Yes& test(int);
197
198 // Fallback, default to returning a No.
199 template<typename C> static No& test(...);
200public:
201 enum { value = (sizeof(test<T>(0)) == sizeof(Yes)
202 && sizeof(T) == (Rows*Cols)*sizeof(Base))
203 };
204};
205
206
207/// C arrays of just the right length also are qualified for has_double_subscript.
208template<typename Base, int Rows, int Cols>
209struct has_double_subscript<Base[Rows][Cols], Base, Rows, Cols> : public std::true_type { };
210
211/// @}
212
213#endif
214
215IMATH_INTERNAL_NAMESPACE_HEADER_EXIT
216
217#endif // INCLUDED_IMATHTYPETRAITS_H
218

source code of include/Imath/ImathTypeTraits.h