-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Different approach: * New file ImathTypeTraits.h now collects all the type traits (including a couple things we had previously put in ImathPlatform.h). * New traits: has_subscript, has_xy, has_xyz, has_xyzw. * New test file: testInterop.{h,cpp} * Plain C arrays[3] work now. * Classes that have .x, .y, .z member variables (but no subscripting) work now. * Classes that have BOTH subscripting and named members work! * It's easy to correct false positives and false negatives. I still have only prototyped for Vec3, if people like the design, I will extend to the other relevant types. Signed-off-by: Larry Gritz <[email protected]>
- Loading branch information
Showing
9 changed files
with
470 additions
and
123 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
// | ||
// 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 <type_traits> | ||
|
||
#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 <bool B, class T = void> | ||
using enable_if_t = typename std::enable_if<B, T>::type; | ||
#endif | ||
|
||
|
||
/// An enable_if helper to be used in template parameters which results in | ||
/// much shorter symbols. | ||
#define IMATH_ENABLE_IF(...) Imath::enable_if_t<(__VA_ARGS__), int> = 0 | ||
|
||
|
||
|
||
/// @{ | ||
/// @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<T,Base>`, `has_xyz<T,Base>`, `has_xyzw<T,Base>` 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<T,Base,N>` 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<T>` 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<mytype, float, 3> : 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<typename B, int N> | ||
/// struct Imath::has_subscript<mytype, B, N> : public std::false_type { }; | ||
/// | ||
|
||
|
||
/// `has_xy<T,Base>::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 <typename T, typename Base> | ||
struct has_xy { | ||
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<decltype(C().x), Base>::value), | ||
IMATH_ENABLE_IF(std::is_same<decltype(C().y), Base>::value)> | ||
static Yes& test(int); | ||
|
||
// Fallback, default to returning a No. | ||
template<typename C> static No& test(...); | ||
public: | ||
enum { value = (sizeof(test<T>(0)) == sizeof(Yes) | ||
&& sizeof(T) == 2*sizeof(Base)) | ||
}; | ||
}; | ||
|
||
|
||
/// `has_xyz<T,Base>::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 <typename T, typename Base> | ||
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<decltype(C().x), Base>::value), | ||
IMATH_ENABLE_IF(std::is_same<decltype(C().y), Base>::value), | ||
IMATH_ENABLE_IF(std::is_same<decltype(C().z), Base>::value)> | ||
static Yes& test(int); | ||
|
||
// Fallback, default to returning a No. | ||
template<typename C> static No& test(...); | ||
public: | ||
enum { value = (sizeof(test<T>(0)) == sizeof(Yes) | ||
&& sizeof(T) == 3*sizeof(Base)) | ||
}; | ||
}; | ||
|
||
|
||
/// `has_xyzw<T,Base>::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 <typename T, typename Base> | ||
struct has_xyzw { | ||
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<decltype(C().x), Base>::value), | ||
IMATH_ENABLE_IF(std::is_same<decltype(C().y), Base>::value), | ||
IMATH_ENABLE_IF(std::is_same<decltype(C().z), Base>::value), | ||
IMATH_ENABLE_IF(std::is_same<decltype(C().w), Base>::value)> | ||
static Yes& test(int); | ||
|
||
// Fallback, default to returning a No. | ||
template<typename C> static No& test(...); | ||
public: | ||
enum { value = (sizeof(test<T>(0)) == sizeof(Yes) | ||
&& sizeof(T) == 4*sizeof(Base)) | ||
}; | ||
}; | ||
|
||
|
||
|
||
/// `has_subscript<T,Base,N>::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 `N` `Base` values. | ||
template <typename T, typename Base, int N> | ||
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<decltype(C()[0])>::type, Base>::value)> | ||
static Yes& test(int); | ||
|
||
// Fallback, default to returning a No. | ||
template<typename C> static No& test(...); | ||
public: | ||
enum { value = (sizeof(test<T>(0)) == sizeof(Yes) | ||
&& sizeof(T) == N*sizeof(Base)) | ||
}; | ||
}; | ||
|
||
|
||
/// C arrays of just the right length also are qualified for has_subscript. | ||
template<typename Base, int N> | ||
struct has_subscript<Base[N], Base, N> : public std::true_type { }; | ||
|
||
/// @} | ||
|
||
|
||
IMATH_INTERNAL_NAMESPACE_HEADER_EXIT | ||
|
||
#endif // INCLUDED_IMATHTYPETRAITS_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.