dawn-cmake/src/tint/castable.h

818 lines
32 KiB
C
Raw Normal View History

// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_TINT_CASTABLE_H_
#define SRC_TINT_CASTABLE_H_
Castable: Optimizations using bloomfilters Calculate a hashcode for each TypeInfo, use these hashcodes to quickly eliminate full base class walks for As() and Is(). Use the hashcodes to optimize IsAnyOf() calls where the result is false. Benchmarks: A: base.bench B: new.bench Test name | Δ (A → B) | % (A → B) --------------------------------------+--------------+----------- ParseWGSL/"particles.wgsl" | -78.84µs | -3.2% GenerateWGSL/"particles.wgsl" | -11.264µs | -5.8% GenerateGLSL/"particles.wgsl" | -2.610127ms | -7.2% GenerateMSL/"simple_compute.wgsl" | -41.187µs | -7.2% GenerateMSL/"particles.wgsl" | -818.689µs | -7.2% GenerateGLSL/"simple_compute.wgsl" | -48.362µs | -7.4% GenerateGLSL/"simple_vertex.wgsl" | -49.992µs | -7.7% GenerateSPIRV/"simple_compute.wgsl" | -42.869µs | -7.8% GenerateSPIRV/"particles.wgsl" | -835.423µs | -7.9% GenerateSPIRV/"simple_fragment.wgsl" | -33.868µs | -8.1% GenerateGLSL/"simple_fragment.wgsl" | -51.368µs | -8.2% GenerateSPIRV/"simple_vertex.wgsl" | -39.096µs | -8.3% GenerateHLSL/"particles.wgsl" | -1.437747ms | -8.3% GenerateMSL/"simple_vertex.wgsl" | -57.232µs | -8.5% GenerateHLSL/"simple_compute.wgsl" | -85.981µs | -8.9% GenerateHLSL/"simple_fragment.wgsl" | -73.095µs | -9.2% GenerateMSL/"simple_fragment.wgsl" | -61.257µs | -9.3% GenerateHLSL/"simple_vertex.wgsl" | -76.661µs | -9.4% Change-Id: Idb03bf871b08274b5b52ef55e41450ed3448a60f Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/76960 Reviewed-by: David Neto <dneto@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Ben Clayton <bclayton@google.com>
2022-01-24 13:07:20 -08:00
#include <stdint.h>
#include <functional>
#include <tuple>
#include <utility>
#include "src/tint/traits.h"
#include "src/tint/utils/bitcast.h"
#include "src/tint/utils/crc32.h"
#include "src/tint/utils/defer.h"
#if defined(__clang__)
/// Temporarily disable certain warnings when using Castable API
#define TINT_CASTABLE_PUSH_DISABLE_WARNINGS() \
_Pragma("clang diagnostic push") /**/ \
_Pragma("clang diagnostic ignored \"-Wundefined-var-template\"") /**/ \
static_assert(true, "require extra semicolon")
/// Restore disabled warnings
#define TINT_CASTABLE_POP_DISABLE_WARNINGS() \
_Pragma("clang diagnostic pop") /**/ \
static_assert(true, "require extra semicolon")
#else
#define TINT_CASTABLE_PUSH_DISABLE_WARNINGS() static_assert(true, "require extra semicolon")
#define TINT_CASTABLE_POP_DISABLE_WARNINGS() static_assert(true, "require extra semicolon")
#endif
TINT_CASTABLE_PUSH_DISABLE_WARNINGS();
// Forward declarations
namespace tint {
Castable: Optimizations using bloomfilters Calculate a hashcode for each TypeInfo, use these hashcodes to quickly eliminate full base class walks for As() and Is(). Use the hashcodes to optimize IsAnyOf() calls where the result is false. Benchmarks: A: base.bench B: new.bench Test name | Δ (A → B) | % (A → B) --------------------------------------+--------------+----------- ParseWGSL/"particles.wgsl" | -78.84µs | -3.2% GenerateWGSL/"particles.wgsl" | -11.264µs | -5.8% GenerateGLSL/"particles.wgsl" | -2.610127ms | -7.2% GenerateMSL/"simple_compute.wgsl" | -41.187µs | -7.2% GenerateMSL/"particles.wgsl" | -818.689µs | -7.2% GenerateGLSL/"simple_compute.wgsl" | -48.362µs | -7.4% GenerateGLSL/"simple_vertex.wgsl" | -49.992µs | -7.7% GenerateSPIRV/"simple_compute.wgsl" | -42.869µs | -7.8% GenerateSPIRV/"particles.wgsl" | -835.423µs | -7.9% GenerateSPIRV/"simple_fragment.wgsl" | -33.868µs | -8.1% GenerateGLSL/"simple_fragment.wgsl" | -51.368µs | -8.2% GenerateSPIRV/"simple_vertex.wgsl" | -39.096µs | -8.3% GenerateHLSL/"particles.wgsl" | -1.437747ms | -8.3% GenerateMSL/"simple_vertex.wgsl" | -57.232µs | -8.5% GenerateHLSL/"simple_compute.wgsl" | -85.981µs | -8.9% GenerateHLSL/"simple_fragment.wgsl" | -73.095µs | -9.2% GenerateMSL/"simple_fragment.wgsl" | -61.257µs | -9.3% GenerateHLSL/"simple_vertex.wgsl" | -76.661µs | -9.4% Change-Id: Idb03bf871b08274b5b52ef55e41450ed3448a60f Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/76960 Reviewed-by: David Neto <dneto@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Ben Clayton <bclayton@google.com>
2022-01-24 13:07:20 -08:00
class CastableBase;
/// Ignore is used as a special type used for skipping over types for trait
/// helper functions.
class Ignore {};
} // namespace tint
namespace tint::detail {
template <typename T>
struct TypeInfoOf;
} // namespace tint::detail
namespace tint {
/// True if all template types that are not Ignore derive from CastableBase
template <typename... TYPES>
static constexpr bool IsCastable =
((traits::IsTypeOrDerived<TYPES, CastableBase> || std::is_same_v<TYPES, Ignore>)&&...) &&
!(std::is_same_v<TYPES, Ignore> && ...);
/// Helper macro to instantiate the TypeInfo<T> template for `CLASS`.
#define TINT_INSTANTIATE_TYPEINFO(CLASS) \
TINT_CASTABLE_PUSH_DISABLE_WARNINGS(); \
template <> \
const tint::TypeInfo tint::detail::TypeInfoOf<CLASS>::info{ \
&tint::detail::TypeInfoOf<CLASS::TrueBase>::info, \
#CLASS, \
tint::TypeInfo::HashCodeOf<CLASS>(), \
tint::TypeInfo::FullHashCodeOf<CLASS>(), \
}; \
TINT_CASTABLE_POP_DISABLE_WARNINGS()
Castable: Optimizations using bloomfilters Calculate a hashcode for each TypeInfo, use these hashcodes to quickly eliminate full base class walks for As() and Is(). Use the hashcodes to optimize IsAnyOf() calls where the result is false. Benchmarks: A: base.bench B: new.bench Test name | Δ (A → B) | % (A → B) --------------------------------------+--------------+----------- ParseWGSL/"particles.wgsl" | -78.84µs | -3.2% GenerateWGSL/"particles.wgsl" | -11.264µs | -5.8% GenerateGLSL/"particles.wgsl" | -2.610127ms | -7.2% GenerateMSL/"simple_compute.wgsl" | -41.187µs | -7.2% GenerateMSL/"particles.wgsl" | -818.689µs | -7.2% GenerateGLSL/"simple_compute.wgsl" | -48.362µs | -7.4% GenerateGLSL/"simple_vertex.wgsl" | -49.992µs | -7.7% GenerateSPIRV/"simple_compute.wgsl" | -42.869µs | -7.8% GenerateSPIRV/"particles.wgsl" | -835.423µs | -7.9% GenerateSPIRV/"simple_fragment.wgsl" | -33.868µs | -8.1% GenerateGLSL/"simple_fragment.wgsl" | -51.368µs | -8.2% GenerateSPIRV/"simple_vertex.wgsl" | -39.096µs | -8.3% GenerateHLSL/"particles.wgsl" | -1.437747ms | -8.3% GenerateMSL/"simple_vertex.wgsl" | -57.232µs | -8.5% GenerateHLSL/"simple_compute.wgsl" | -85.981µs | -8.9% GenerateHLSL/"simple_fragment.wgsl" | -73.095µs | -9.2% GenerateMSL/"simple_fragment.wgsl" | -61.257µs | -9.3% GenerateHLSL/"simple_vertex.wgsl" | -76.661µs | -9.4% Change-Id: Idb03bf871b08274b5b52ef55e41450ed3448a60f Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/76960 Reviewed-by: David Neto <dneto@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Ben Clayton <bclayton@google.com>
2022-01-24 13:07:20 -08:00
/// Bit flags that can be passed to the template parameter `FLAGS` of Is() and
/// As().
enum CastFlags {
/// Disables the static_assert() inside Is(), that compile-time-verifies that
/// the cast is possible. This flag may be useful for highly-generic template
/// code that needs to compile for template permutations that generate
/// impossible casts.
kDontErrorOnImpossibleCast = 1,
Castable: Optimizations using bloomfilters Calculate a hashcode for each TypeInfo, use these hashcodes to quickly eliminate full base class walks for As() and Is(). Use the hashcodes to optimize IsAnyOf() calls where the result is false. Benchmarks: A: base.bench B: new.bench Test name | Δ (A → B) | % (A → B) --------------------------------------+--------------+----------- ParseWGSL/"particles.wgsl" | -78.84µs | -3.2% GenerateWGSL/"particles.wgsl" | -11.264µs | -5.8% GenerateGLSL/"particles.wgsl" | -2.610127ms | -7.2% GenerateMSL/"simple_compute.wgsl" | -41.187µs | -7.2% GenerateMSL/"particles.wgsl" | -818.689µs | -7.2% GenerateGLSL/"simple_compute.wgsl" | -48.362µs | -7.4% GenerateGLSL/"simple_vertex.wgsl" | -49.992µs | -7.7% GenerateSPIRV/"simple_compute.wgsl" | -42.869µs | -7.8% GenerateSPIRV/"particles.wgsl" | -835.423µs | -7.9% GenerateSPIRV/"simple_fragment.wgsl" | -33.868µs | -8.1% GenerateGLSL/"simple_fragment.wgsl" | -51.368µs | -8.2% GenerateSPIRV/"simple_vertex.wgsl" | -39.096µs | -8.3% GenerateHLSL/"particles.wgsl" | -1.437747ms | -8.3% GenerateMSL/"simple_vertex.wgsl" | -57.232µs | -8.5% GenerateHLSL/"simple_compute.wgsl" | -85.981µs | -8.9% GenerateHLSL/"simple_fragment.wgsl" | -73.095µs | -9.2% GenerateMSL/"simple_fragment.wgsl" | -61.257µs | -9.3% GenerateHLSL/"simple_vertex.wgsl" | -76.661µs | -9.4% Change-Id: Idb03bf871b08274b5b52ef55e41450ed3448a60f Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/76960 Reviewed-by: David Neto <dneto@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Ben Clayton <bclayton@google.com>
2022-01-24 13:07:20 -08:00
};
/// TypeInfo holds type information for a Castable type.
struct TypeInfo {
/// The type of a hash code
using HashCode = uint64_t;
/// The base class of this type
const TypeInfo* base;
/// The type name
const char* name;
/// The type hash code
const HashCode hashcode;
/// The type hash code bitwise-or'd with all ancestor's hashcodes.
const HashCode full_hashcode;
/// @returns true if `type` derives from the class `TO`
/// @param object the object type to test from, which must be, or derive from
/// type `FROM`.
/// @see CastFlags
template <typename TO, typename FROM, int FLAGS = 0>
static inline bool Is(const tint::TypeInfo* object) {
constexpr const bool downcast = std::is_base_of<FROM, TO>::value;
constexpr const bool upcast = std::is_base_of<TO, FROM>::value;
constexpr const bool nocast = std::is_same<FROM, TO>::value;
constexpr const bool assert_is_castable = (FLAGS & kDontErrorOnImpossibleCast) == 0;
static_assert(upcast || downcast || nocast || !assert_is_castable, "impossible cast");
return upcast || nocast || object->Is<TO>();
}
/// @returns true if this type derives from the class `T`
template <typename T>
inline bool Is() const {
auto* type = &Of<std::remove_cv_t<T>>();
if constexpr (std::is_final_v<T>) {
// T is final, so nothing can derive from T.
// We do not need to check ancestors, only whether this type is equal to the type T.
return type == this;
} else {
return Is(type);
}
}
/// @param type the test type info
/// @returns true if the class with this TypeInfo is of, or derives from the
/// class with the given TypeInfo.
inline bool Is(const tint::TypeInfo* type) const {
// Optimization: Check whether the all the bits of the type's hashcode can
// be found in the full_hashcode. If a single bit is missing, then we
// can quickly tell that that this TypeInfo does not derive from `type`.
if ((full_hashcode & type->hashcode) != type->hashcode) {
return false;
}
// Walk the base types, starting with this TypeInfo, to see if any of the pointers match
// `type`.
for (auto* ti = this; ti != nullptr; ti = ti->base) {
if (ti == type) {
return true;
}
}
return false;
Castable: Optimizations using bloomfilters Calculate a hashcode for each TypeInfo, use these hashcodes to quickly eliminate full base class walks for As() and Is(). Use the hashcodes to optimize IsAnyOf() calls where the result is false. Benchmarks: A: base.bench B: new.bench Test name | Δ (A → B) | % (A → B) --------------------------------------+--------------+----------- ParseWGSL/"particles.wgsl" | -78.84µs | -3.2% GenerateWGSL/"particles.wgsl" | -11.264µs | -5.8% GenerateGLSL/"particles.wgsl" | -2.610127ms | -7.2% GenerateMSL/"simple_compute.wgsl" | -41.187µs | -7.2% GenerateMSL/"particles.wgsl" | -818.689µs | -7.2% GenerateGLSL/"simple_compute.wgsl" | -48.362µs | -7.4% GenerateGLSL/"simple_vertex.wgsl" | -49.992µs | -7.7% GenerateSPIRV/"simple_compute.wgsl" | -42.869µs | -7.8% GenerateSPIRV/"particles.wgsl" | -835.423µs | -7.9% GenerateSPIRV/"simple_fragment.wgsl" | -33.868µs | -8.1% GenerateGLSL/"simple_fragment.wgsl" | -51.368µs | -8.2% GenerateSPIRV/"simple_vertex.wgsl" | -39.096µs | -8.3% GenerateHLSL/"particles.wgsl" | -1.437747ms | -8.3% GenerateMSL/"simple_vertex.wgsl" | -57.232µs | -8.5% GenerateHLSL/"simple_compute.wgsl" | -85.981µs | -8.9% GenerateHLSL/"simple_fragment.wgsl" | -73.095µs | -9.2% GenerateMSL/"simple_fragment.wgsl" | -61.257µs | -9.3% GenerateHLSL/"simple_vertex.wgsl" | -76.661µs | -9.4% Change-Id: Idb03bf871b08274b5b52ef55e41450ed3448a60f Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/76960 Reviewed-by: David Neto <dneto@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Ben Clayton <bclayton@google.com>
2022-01-24 13:07:20 -08:00
}
/// @returns the static TypeInfo for the type T
template <typename T>
static const TypeInfo& Of() {
return detail::TypeInfoOf<std::remove_cv_t<T>>::info;
Castable: Optimizations using bloomfilters Calculate a hashcode for each TypeInfo, use these hashcodes to quickly eliminate full base class walks for As() and Is(). Use the hashcodes to optimize IsAnyOf() calls where the result is false. Benchmarks: A: base.bench B: new.bench Test name | Δ (A → B) | % (A → B) --------------------------------------+--------------+----------- ParseWGSL/"particles.wgsl" | -78.84µs | -3.2% GenerateWGSL/"particles.wgsl" | -11.264µs | -5.8% GenerateGLSL/"particles.wgsl" | -2.610127ms | -7.2% GenerateMSL/"simple_compute.wgsl" | -41.187µs | -7.2% GenerateMSL/"particles.wgsl" | -818.689µs | -7.2% GenerateGLSL/"simple_compute.wgsl" | -48.362µs | -7.4% GenerateGLSL/"simple_vertex.wgsl" | -49.992µs | -7.7% GenerateSPIRV/"simple_compute.wgsl" | -42.869µs | -7.8% GenerateSPIRV/"particles.wgsl" | -835.423µs | -7.9% GenerateSPIRV/"simple_fragment.wgsl" | -33.868µs | -8.1% GenerateGLSL/"simple_fragment.wgsl" | -51.368µs | -8.2% GenerateSPIRV/"simple_vertex.wgsl" | -39.096µs | -8.3% GenerateHLSL/"particles.wgsl" | -1.437747ms | -8.3% GenerateMSL/"simple_vertex.wgsl" | -57.232µs | -8.5% GenerateHLSL/"simple_compute.wgsl" | -85.981µs | -8.9% GenerateHLSL/"simple_fragment.wgsl" | -73.095µs | -9.2% GenerateMSL/"simple_fragment.wgsl" | -61.257µs | -9.3% GenerateHLSL/"simple_vertex.wgsl" | -76.661µs | -9.4% Change-Id: Idb03bf871b08274b5b52ef55e41450ed3448a60f Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/76960 Reviewed-by: David Neto <dneto@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Ben Clayton <bclayton@google.com>
2022-01-24 13:07:20 -08:00
}
/// @returns a compile-time hashcode for the type `T`.
/// @note the returned hashcode will have at most 2 bits set, as the hashes
/// are expected to be used in bloom-filters which will quickly saturate when
/// multiple hashcodes are bitwise-or'd together.
template <typename T>
static constexpr HashCode HashCodeOf() {
static_assert(IsCastable<T>, "T is not Castable");
static_assert(std::is_same_v<T, std::remove_cv_t<T>>,
"Strip const / volatile decorations before calling HashCodeOf");
/// Use the compiler's "pretty" function name, which includes the template
/// type, to obtain a unique hash value.
Castable: Optimizations using bloomfilters Calculate a hashcode for each TypeInfo, use these hashcodes to quickly eliminate full base class walks for As() and Is(). Use the hashcodes to optimize IsAnyOf() calls where the result is false. Benchmarks: A: base.bench B: new.bench Test name | Δ (A → B) | % (A → B) --------------------------------------+--------------+----------- ParseWGSL/"particles.wgsl" | -78.84µs | -3.2% GenerateWGSL/"particles.wgsl" | -11.264µs | -5.8% GenerateGLSL/"particles.wgsl" | -2.610127ms | -7.2% GenerateMSL/"simple_compute.wgsl" | -41.187µs | -7.2% GenerateMSL/"particles.wgsl" | -818.689µs | -7.2% GenerateGLSL/"simple_compute.wgsl" | -48.362µs | -7.4% GenerateGLSL/"simple_vertex.wgsl" | -49.992µs | -7.7% GenerateSPIRV/"simple_compute.wgsl" | -42.869µs | -7.8% GenerateSPIRV/"particles.wgsl" | -835.423µs | -7.9% GenerateSPIRV/"simple_fragment.wgsl" | -33.868µs | -8.1% GenerateGLSL/"simple_fragment.wgsl" | -51.368µs | -8.2% GenerateSPIRV/"simple_vertex.wgsl" | -39.096µs | -8.3% GenerateHLSL/"particles.wgsl" | -1.437747ms | -8.3% GenerateMSL/"simple_vertex.wgsl" | -57.232µs | -8.5% GenerateHLSL/"simple_compute.wgsl" | -85.981µs | -8.9% GenerateHLSL/"simple_fragment.wgsl" | -73.095µs | -9.2% GenerateMSL/"simple_fragment.wgsl" | -61.257µs | -9.3% GenerateHLSL/"simple_vertex.wgsl" | -76.661µs | -9.4% Change-Id: Idb03bf871b08274b5b52ef55e41450ed3448a60f Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/76960 Reviewed-by: David Neto <dneto@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Ben Clayton <bclayton@google.com>
2022-01-24 13:07:20 -08:00
#ifdef _MSC_VER
constexpr uint32_t crc = utils::CRC32(__FUNCSIG__);
Castable: Optimizations using bloomfilters Calculate a hashcode for each TypeInfo, use these hashcodes to quickly eliminate full base class walks for As() and Is(). Use the hashcodes to optimize IsAnyOf() calls where the result is false. Benchmarks: A: base.bench B: new.bench Test name | Δ (A → B) | % (A → B) --------------------------------------+--------------+----------- ParseWGSL/"particles.wgsl" | -78.84µs | -3.2% GenerateWGSL/"particles.wgsl" | -11.264µs | -5.8% GenerateGLSL/"particles.wgsl" | -2.610127ms | -7.2% GenerateMSL/"simple_compute.wgsl" | -41.187µs | -7.2% GenerateMSL/"particles.wgsl" | -818.689µs | -7.2% GenerateGLSL/"simple_compute.wgsl" | -48.362µs | -7.4% GenerateGLSL/"simple_vertex.wgsl" | -49.992µs | -7.7% GenerateSPIRV/"simple_compute.wgsl" | -42.869µs | -7.8% GenerateSPIRV/"particles.wgsl" | -835.423µs | -7.9% GenerateSPIRV/"simple_fragment.wgsl" | -33.868µs | -8.1% GenerateGLSL/"simple_fragment.wgsl" | -51.368µs | -8.2% GenerateSPIRV/"simple_vertex.wgsl" | -39.096µs | -8.3% GenerateHLSL/"particles.wgsl" | -1.437747ms | -8.3% GenerateMSL/"simple_vertex.wgsl" | -57.232µs | -8.5% GenerateHLSL/"simple_compute.wgsl" | -85.981µs | -8.9% GenerateHLSL/"simple_fragment.wgsl" | -73.095µs | -9.2% GenerateMSL/"simple_fragment.wgsl" | -61.257µs | -9.3% GenerateHLSL/"simple_vertex.wgsl" | -76.661µs | -9.4% Change-Id: Idb03bf871b08274b5b52ef55e41450ed3448a60f Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/76960 Reviewed-by: David Neto <dneto@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Ben Clayton <bclayton@google.com>
2022-01-24 13:07:20 -08:00
#else
constexpr uint32_t crc = utils::CRC32(__PRETTY_FUNCTION__);
Castable: Optimizations using bloomfilters Calculate a hashcode for each TypeInfo, use these hashcodes to quickly eliminate full base class walks for As() and Is(). Use the hashcodes to optimize IsAnyOf() calls where the result is false. Benchmarks: A: base.bench B: new.bench Test name | Δ (A → B) | % (A → B) --------------------------------------+--------------+----------- ParseWGSL/"particles.wgsl" | -78.84µs | -3.2% GenerateWGSL/"particles.wgsl" | -11.264µs | -5.8% GenerateGLSL/"particles.wgsl" | -2.610127ms | -7.2% GenerateMSL/"simple_compute.wgsl" | -41.187µs | -7.2% GenerateMSL/"particles.wgsl" | -818.689µs | -7.2% GenerateGLSL/"simple_compute.wgsl" | -48.362µs | -7.4% GenerateGLSL/"simple_vertex.wgsl" | -49.992µs | -7.7% GenerateSPIRV/"simple_compute.wgsl" | -42.869µs | -7.8% GenerateSPIRV/"particles.wgsl" | -835.423µs | -7.9% GenerateSPIRV/"simple_fragment.wgsl" | -33.868µs | -8.1% GenerateGLSL/"simple_fragment.wgsl" | -51.368µs | -8.2% GenerateSPIRV/"simple_vertex.wgsl" | -39.096µs | -8.3% GenerateHLSL/"particles.wgsl" | -1.437747ms | -8.3% GenerateMSL/"simple_vertex.wgsl" | -57.232µs | -8.5% GenerateHLSL/"simple_compute.wgsl" | -85.981µs | -8.9% GenerateHLSL/"simple_fragment.wgsl" | -73.095µs | -9.2% GenerateMSL/"simple_fragment.wgsl" | -61.257µs | -9.3% GenerateHLSL/"simple_vertex.wgsl" | -76.661µs | -9.4% Change-Id: Idb03bf871b08274b5b52ef55e41450ed3448a60f Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/76960 Reviewed-by: David Neto <dneto@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Ben Clayton <bclayton@google.com>
2022-01-24 13:07:20 -08:00
#endif
constexpr uint32_t bit_a = (crc & 63);
constexpr uint32_t bit_b = ((crc >> 6) & 63);
return (static_cast<HashCode>(1) << bit_a) | (static_cast<HashCode>(1) << bit_b);
}
/// @returns the hashcode of the given type, bitwise-or'd with the hashcodes
/// of all base classes.
template <typename T>
static constexpr HashCode FullHashCodeOf() {
if constexpr (std::is_same_v<T, CastableBase>) {
return HashCodeOf<CastableBase>();
} else {
return HashCodeOf<T>() | FullHashCodeOf<typename T::TrueBase>();
}
}
/// @returns the bitwise-or'd hashcodes of all the types of the tuple `TUPLE`.
/// @see HashCodeOf
template <typename TUPLE>
static constexpr HashCode CombinedHashCodeOfTuple() {
constexpr auto kCount = std::tuple_size_v<TUPLE>;
if constexpr (kCount == 0) {
return 0;
} else if constexpr (kCount == 1) {
return HashCodeOf<std::remove_cv_t<std::tuple_element_t<0, TUPLE>>>();
} else {
constexpr auto kMid = kCount / 2;
return CombinedHashCodeOfTuple<traits::SliceTuple<0, kMid, TUPLE>>() |
CombinedHashCodeOfTuple<traits::SliceTuple<kMid, kCount - kMid, TUPLE>>();
}
}
/// @returns the bitwise-or'd hashcodes of all the template parameter types.
/// @see HashCodeOf
template <typename... TYPES>
static constexpr HashCode CombinedHashCodeOf() {
return CombinedHashCodeOfTuple<std::tuple<TYPES...>>();
}
/// @returns true if this TypeInfo is of, or derives from any of the types in
/// `TUPLE`.
template <typename TUPLE>
inline bool IsAnyOfTuple() const {
constexpr auto kCount = std::tuple_size_v<TUPLE>;
if constexpr (kCount == 0) {
return false;
} else if constexpr (kCount == 1) {
return Is<std::tuple_element_t<0, TUPLE>>();
} else if constexpr (kCount == 2) {
return Is<std::tuple_element_t<0, TUPLE>>() || Is<std::tuple_element_t<1, TUPLE>>();
} else if constexpr (kCount == 3) {
return Is<std::tuple_element_t<0, TUPLE>>() || Is<std::tuple_element_t<1, TUPLE>>() ||
Is<std::tuple_element_t<2, TUPLE>>();
} else {
// Optimization: Compare the object's hashcode to the bitwise-or of all
// the tested type's hashcodes. If there's no intersection of bits in
// the two masks, then we can guarantee that the type is not in `TO`.
if (full_hashcode & TypeInfo::CombinedHashCodeOfTuple<TUPLE>()) {
// Possibly one of the types in `TUPLE`.
// Split the search in two, and scan each block.
static constexpr auto kMid = kCount / 2;
return IsAnyOfTuple<traits::SliceTuple<0, kMid, TUPLE>>() ||
IsAnyOfTuple<traits::SliceTuple<kMid, kCount - kMid, TUPLE>>();
}
return false;
}
}
/// @returns true if this TypeInfo is of, or derives from any of the types in
/// `TYPES`.
template <typename... TYPES>
inline bool IsAnyOf() const {
return IsAnyOfTuple<std::tuple<TYPES...>>();
Castable: Optimizations using bloomfilters Calculate a hashcode for each TypeInfo, use these hashcodes to quickly eliminate full base class walks for As() and Is(). Use the hashcodes to optimize IsAnyOf() calls where the result is false. Benchmarks: A: base.bench B: new.bench Test name | Δ (A → B) | % (A → B) --------------------------------------+--------------+----------- ParseWGSL/"particles.wgsl" | -78.84µs | -3.2% GenerateWGSL/"particles.wgsl" | -11.264µs | -5.8% GenerateGLSL/"particles.wgsl" | -2.610127ms | -7.2% GenerateMSL/"simple_compute.wgsl" | -41.187µs | -7.2% GenerateMSL/"particles.wgsl" | -818.689µs | -7.2% GenerateGLSL/"simple_compute.wgsl" | -48.362µs | -7.4% GenerateGLSL/"simple_vertex.wgsl" | -49.992µs | -7.7% GenerateSPIRV/"simple_compute.wgsl" | -42.869µs | -7.8% GenerateSPIRV/"particles.wgsl" | -835.423µs | -7.9% GenerateSPIRV/"simple_fragment.wgsl" | -33.868µs | -8.1% GenerateGLSL/"simple_fragment.wgsl" | -51.368µs | -8.2% GenerateSPIRV/"simple_vertex.wgsl" | -39.096µs | -8.3% GenerateHLSL/"particles.wgsl" | -1.437747ms | -8.3% GenerateMSL/"simple_vertex.wgsl" | -57.232µs | -8.5% GenerateHLSL/"simple_compute.wgsl" | -85.981µs | -8.9% GenerateHLSL/"simple_fragment.wgsl" | -73.095µs | -9.2% GenerateMSL/"simple_fragment.wgsl" | -61.257µs | -9.3% GenerateHLSL/"simple_vertex.wgsl" | -76.661µs | -9.4% Change-Id: Idb03bf871b08274b5b52ef55e41450ed3448a60f Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/76960 Reviewed-by: David Neto <dneto@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Ben Clayton <bclayton@google.com>
2022-01-24 13:07:20 -08:00
}
};
namespace detail {
/// TypeInfoOf contains a single TypeInfo field for the type T.
/// TINT_INSTANTIATE_TYPEINFO() must be defined in a .cpp file for each type
/// `T`.
template <typename T>
struct TypeInfoOf {
/// The unique TypeInfo for the type T.
static const TypeInfo info;
};
/// A placeholder structure used for template parameters that need a default
/// type, but can always be automatically inferred.
struct Infer;
} // namespace detail
/// @returns true if `obj` is a valid pointer, and is of, or derives from the
/// class `TO`
/// @param obj the object to test from
/// @see CastFlags
template <typename TO, int FLAGS = 0, typename FROM = detail::Infer>
inline bool Is(FROM* obj) {
if (obj == nullptr) {
return false;
}
return TypeInfo::Is<TO, FROM, FLAGS>(&obj->TypeInfo());
}
/// @returns true if `obj` is a valid pointer, and is of, or derives from the
/// type `TYPE`, and pred(const TYPE*) returns true
/// @param obj the object to test from
/// @param pred predicate function with signature `bool(const TYPE*)` called iff
/// object is of, or derives from the class `TYPE`.
/// @see CastFlags
template <typename TYPE, int FLAGS = 0, typename OBJ = detail::Infer, typename Pred = detail::Infer>
inline bool Is(OBJ* obj, Pred&& pred) {
return Is<TYPE, FLAGS, OBJ>(obj) && pred(static_cast<std::add_const_t<TYPE>*>(obj));
}
/// @returns true if `obj` is a valid pointer, and is of, or derives from any of
/// the types in `TYPES`.OBJ
/// @param obj the object to query.
template <typename... TYPES, typename OBJ>
inline bool IsAnyOf(OBJ* obj) {
if (!obj) {
return false;
}
return obj->TypeInfo().template IsAnyOf<TYPES...>();
}
/// @returns obj dynamically cast to the type `TO` or `nullptr` if
/// this object does not derive from `TO`.
/// @param obj the object to cast from
/// @see CastFlags
template <typename TO, int FLAGS = 0, typename FROM = detail::Infer>
inline TO* As(FROM* obj) {
auto* as_castable = static_cast<CastableBase*>(obj);
return Is<TO, FLAGS>(obj) ? static_cast<TO*>(as_castable) : nullptr;
}
/// @returns obj dynamically cast to the type `TO` or `nullptr` if
/// this object does not derive from `TO`.
/// @param obj the object to cast from
/// @see CastFlags
template <typename TO, int FLAGS = 0, typename FROM = detail::Infer>
inline const TO* As(const FROM* obj) {
auto* as_castable = static_cast<const CastableBase*>(obj);
return Is<TO, FLAGS>(obj) ? static_cast<const TO*>(as_castable) : nullptr;
}
/// CastableBase is the base class for all Castable objects.
/// It is not encouraged to directly derive from CastableBase without using the
/// Castable helper template.
/// @see Castable
class CastableBase {
public:
/// Copy constructor
CastableBase(const CastableBase&);
/// Destructor
virtual ~CastableBase();
/// Copy assignment
/// @param other the CastableBase to copy
/// @returns the new CastableBase
CastableBase& operator=(const CastableBase& other) = default;
/// @returns the TypeInfo of the object
virtual const tint::TypeInfo& TypeInfo() const = 0;
/// @returns true if this object is of, or derives from the class `TO`
template <typename TO>
inline bool Is() const {
return tint::Is<TO>(this);
}
/// @returns true if this object is of, or derives from the class `TO` and
/// pred(const TO*) returns true
/// @param pred predicate function with signature `bool(const TO*)` called iff
/// object is of, or derives from the class `TO`.
template <typename TO, int FLAGS = 0, typename Pred = detail::Infer>
inline bool Is(Pred&& pred) const {
return tint::Is<TO, FLAGS>(this, std::forward<Pred>(pred));
}
/// @returns true if this object is of, or derives from any of the `TO`
/// classes.
template <typename... TO>
inline bool IsAnyOf() const {
return tint::IsAnyOf<TO...>(this);
}
/// @returns this object dynamically cast to the type `TO` or `nullptr` if
/// this object does not derive from `TO`.
/// @see CastFlags
template <typename TO, int FLAGS = 0>
inline TO* As() {
return tint::As<TO, FLAGS>(this);
}
/// @returns this object dynamically cast to the type `TO` or `nullptr` if
/// this object does not derive from `TO`.
/// @see CastFlags
template <typename TO, int FLAGS = 0>
inline const TO* As() const {
return tint::As<const TO, FLAGS>(this);
}
protected:
CastableBase() = default;
};
/// Castable is a helper to derive `CLASS` from `BASE`, automatically
/// implementing the Is() and As() methods, along with a #Base type alias.
///
/// Example usage:
///
/// ```
/// class Animal : public Castable<Animal> {};
///
/// class Sheep : public Castable<Sheep, Animal> {};
///
/// Sheep* cast_to_sheep(Animal* animal) {
/// // You can query whether a Castable is of the given type with Is<T>():
/// printf("animal is a sheep? %s", animal->Is<Sheep>() ? "yes" : "no");
///
/// // You can always just try the cast with As<T>().
/// // If the object is not of the correct type, As<T>() will return nullptr:
/// return animal->As<Sheep>();
/// }
/// ```
template <typename CLASS, typename BASE = CastableBase>
class Castable : public BASE {
public:
// Inherit the `BASE` class constructors.
using BASE::BASE;
/// A type alias for `CLASS` to easily access the `BASE` class members.
/// Base actually aliases to the Castable instead of `BASE` so that you can
/// use Base in the `CLASS` constructor.
using Base = Castable;
/// A type alias for `BASE`.
using TrueBase = BASE;
/// @returns the TypeInfo of the object
const tint::TypeInfo& TypeInfo() const override { return TypeInfo::Of<CLASS>(); }
/// @returns true if this object is of, or derives from the class `TO`
/// @see CastFlags
template <typename TO, int FLAGS = 0>
inline bool Is() const {
return tint::Is<TO, FLAGS>(static_cast<const CLASS*>(this));
}
/// @returns true if this object is of, or derives from the class `TO` and
/// pred(const TO*) returns true
/// @param pred predicate function with signature `bool(const TO*)` called iff
/// object is of, or derives from the class `TO`.
template <int FLAGS = 0, typename Pred = detail::Infer>
inline bool Is(Pred&& pred) const {
using TO = typename std::remove_pointer<traits::ParameterType<Pred, 0>>::type;
return tint::Is<TO, FLAGS>(static_cast<const CLASS*>(this), std::forward<Pred>(pred));
}
/// @returns true if this object is of, or derives from any of the `TO`
/// classes.
template <typename... TO>
inline bool IsAnyOf() const {
return tint::IsAnyOf<TO...>(static_cast<const CLASS*>(this));
}
/// @returns this object dynamically cast to the type `TO` or `nullptr` if
/// this object does not derive from `TO`.
/// @see CastFlags
template <typename TO, int FLAGS = 0>
inline TO* As() {
return tint::As<TO, FLAGS>(this);
}
/// @returns this object dynamically cast to the type `TO` or `nullptr` if
/// this object does not derive from `TO`.
/// @see CastFlags
template <typename TO, int FLAGS = 0>
inline const TO* As() const {
return tint::As<const TO, FLAGS>(this);
}
};
namespace detail {
/// <code>typename CastableCommonBaseImpl<TYPES>::type</code> resolves to the
/// common base class for all of TYPES.
template <typename... TYPES>
struct CastableCommonBaseImpl {};
/// Alias to typename CastableCommonBaseImpl<TYPES>::type
template <typename... TYPES>
using CastableCommonBase = typename detail::CastableCommonBaseImpl<TYPES...>::type;
/// CastableCommonBaseImpl template specialization for a single type
template <typename T>
struct CastableCommonBaseImpl<T> {
/// Common base class of a single type is itself
using type = T;
};
/// CastableCommonBaseImpl A <-> CastableBase specialization
template <typename A>
struct CastableCommonBaseImpl<A, CastableBase> {
/// Common base class for A and CastableBase is CastableBase
using type = CastableBase;
};
/// CastableCommonBaseImpl T <-> Ignore specialization
template <typename T>
struct CastableCommonBaseImpl<T, Ignore> {
/// Resolves to T as the other type is ignored
using type = T;
};
/// CastableCommonBaseImpl Ignore <-> T specialization
template <typename T>
struct CastableCommonBaseImpl<Ignore, T> {
/// Resolves to T as the other type is ignored
using type = T;
};
/// CastableCommonBaseImpl A <-> B specialization
template <typename A, typename B>
struct CastableCommonBaseImpl<A, B> {
/// The common base class for A, B and OTHERS
using type = std::conditional_t<traits::IsTypeOrDerived<A, B>,
B, // A derives from B
CastableCommonBase<A, typename B::TrueBase>>;
};
/// CastableCommonBaseImpl 3+ types specialization
template <typename A, typename B, typename... OTHERS>
struct CastableCommonBaseImpl<A, B, OTHERS...> {
/// The common base class for A, B and OTHERS
using type = CastableCommonBase<CastableCommonBase<A, B>, OTHERS...>;
};
} // namespace detail
/// Resolves to the common most derived type that each of the types in `TYPES`
/// derives from.
template <typename... TYPES>
using CastableCommonBase = detail::CastableCommonBase<TYPES...>;
/// Default can be used as the default case for a Switch(), when all previous
/// cases failed to match.
///
/// Example:
/// ```
/// Switch(object,
/// [&](TypeA*) { /* ... */ },
/// [&](TypeB*) { /* ... */ },
/// [&](Default) { /* If not TypeA or TypeB */ });
/// ```
struct Default {};
namespace detail {
/// Evaluates to the Switch case type being matched by the switch case function
/// `FN`.
/// @note does not handle the Default case
/// @see Switch().
template <typename FN>
using SwitchCaseType = std::remove_pointer_t<traits::ParameterType<std::remove_reference_t<FN>, 0>>;
/// Evaluates to true if the function `FN` has the signature of a Default case
/// in a Switch().
/// @see Switch().
template <typename FN>
inline constexpr bool IsDefaultCase =
std::is_same_v<traits::ParameterType<std::remove_reference_t<FN>, 0>, Default>;
/// Searches the list of Switch cases for a Default case, returning the index of
/// the Default case. If the a Default case is not found in the tuple, then -1
/// is returned.
template <typename TUPLE, std::size_t START_IDX = 0>
constexpr int IndexOfDefaultCase() {
if constexpr (START_IDX < std::tuple_size_v<TUPLE>) {
return IsDefaultCase<std::tuple_element_t<START_IDX, TUPLE>>
? static_cast<int>(START_IDX)
: IndexOfDefaultCase<TUPLE, START_IDX + 1>();
} else {
return -1;
}
}
/// The implementation of Switch() for non-Default cases.
/// Switch splits the cases into two a low and high block of cases, and quickly
/// rules out blocks that cannot match by comparing the TypeInfo::HashCode of
/// the object and the cases in the block. If a block of cases may match the
/// given object's type, then that block is split into two, and the process
/// recurses. When NonDefaultCases() is called with a single case, then As<>
/// will be used to dynamically cast to the case type and if the cast succeeds,
/// then the case handler is called.
/// @returns true if a case handler was found, otherwise false.
template <typename T, typename RETURN_TYPE, typename... CASES>
inline bool NonDefaultCases(T* object,
const TypeInfo* type,
RETURN_TYPE* result,
std::tuple<CASES...>&& cases) {
using Cases = std::tuple<CASES...>;
(void)result; // Not always used, avoid warning.
static constexpr bool kHasReturnType = !std::is_same_v<RETURN_TYPE, void>;
static constexpr size_t kNumCases = sizeof...(CASES);
if constexpr (kNumCases == 0) {
// No cases. Nothing to do.
return false;
} else if constexpr (kNumCases == 1) { // NOLINT: cpplint doesn't understand
// `else if constexpr`
// Single case.
using CaseFunc = std::tuple_element_t<0, Cases>;
static_assert(!IsDefaultCase<CaseFunc>, "NonDefaultCases called with a Default case");
// Attempt to dynamically cast the object to the handler type. If that
// succeeds, call the case handler with the cast object.
using CaseType = SwitchCaseType<CaseFunc>;
if (type->Is<CaseType>()) {
auto* ptr = static_cast<CaseType*>(object);
if constexpr (kHasReturnType) {
new (result) RETURN_TYPE(static_cast<RETURN_TYPE>(std::get<0>(cases)(ptr)));
} else {
std::get<0>(cases)(ptr);
}
return true;
}
return false;
} else {
// Multiple cases.
// Check the hashcode bits to see if there's any possibility of a case
// matching in these cases. If there isn't, we can skip all these cases.
if (type->full_hashcode & TypeInfo::CombinedHashCodeOf<SwitchCaseType<CASES>...>()) {
// There's a possibility. We need to scan further.
// Split the cases into two, and recurse.
constexpr size_t kMid = kNumCases / 2;
return NonDefaultCases(object, type, result, traits::Slice<0, kMid>(cases)) ||
NonDefaultCases(object, type, result,
traits::Slice<kMid, kNumCases - kMid>(cases));
} else {
return false;
}
}
}
/// The implementation of Switch() for all cases.
/// @see NonDefaultCases
template <typename T, typename RETURN_TYPE, typename... CASES>
inline void SwitchCases(T* object, RETURN_TYPE* result, std::tuple<CASES...>&& cases) {
using Cases = std::tuple<CASES...>;
static constexpr int kDefaultIndex = detail::IndexOfDefaultCase<Cases>();
static constexpr bool kHasDefaultCase = kDefaultIndex >= 0;
static constexpr bool kHasReturnType = !std::is_same_v<RETURN_TYPE, void>;
// Static assertions
static constexpr bool kDefaultIsOK =
kDefaultIndex == -1 || kDefaultIndex == static_cast<int>(std::tuple_size_v<Cases> - 1);
static constexpr bool kReturnIsOK =
kHasDefaultCase || !kHasReturnType || std::is_constructible_v<RETURN_TYPE>;
static_assert(kDefaultIsOK, "Default case must be last in Switch()");
static_assert(kReturnIsOK,
"Switch() requires either a Default case or a return type that is either void or "
"default-constructable");
// If the static asserts have fired, don't bother spewing more errors below
static constexpr bool kAllOK = kDefaultIsOK && kReturnIsOK;
if constexpr (kAllOK) {
if (object) {
auto* type = &object->TypeInfo();
if constexpr (kHasDefaultCase) {
// Evaluate non-default cases.
if (!detail::NonDefaultCases<T>(object, type, result,
traits::Slice<0, kDefaultIndex>(cases))) {
// Nothing matched. Evaluate default case.
if constexpr (kHasReturnType) {
new (result) RETURN_TYPE(
static_cast<RETURN_TYPE>(std::get<kDefaultIndex>(cases)({})));
} else {
std::get<kDefaultIndex>(cases)({});
}
}
} else {
if (!detail::NonDefaultCases<T>(object, type, result, std::move(cases))) {
// Nothing matched. No default case.
if constexpr (kHasReturnType) {
new (result) RETURN_TYPE();
}
}
}
} else {
// Object is nullptr, so no cases can match
if constexpr (kHasDefaultCase) {
// Evaluate default case.
if constexpr (kHasReturnType) {
new (result)
RETURN_TYPE(static_cast<RETURN_TYPE>(std::get<kDefaultIndex>(cases)({})));
} else {
std::get<kDefaultIndex>(cases)({});
}
} else {
// No default case, no case can match.
if constexpr (kHasReturnType) {
new (result) RETURN_TYPE();
}
}
}
}
}
/// Resolves to T if T is not nullptr_t, otherwise resolves to Ignore.
template <typename T>
using NullptrToIgnore = std::conditional_t<std::is_same_v<T, std::nullptr_t>, Ignore, T>;
/// Resolves to `const TYPE` if any of `CASE_RETURN_TYPES` are const or
/// pointer-to-const, otherwise resolves to TYPE.
template <typename TYPE, typename... CASE_RETURN_TYPES>
using PropagateReturnConst = std::conditional_t<
// Are any of the pointer-stripped types const?
(std::is_const_v<std::remove_pointer_t<CASE_RETURN_TYPES>> || ...),
const TYPE, // Yes: Apply const to TYPE
TYPE>; // No: Passthrough
/// SwitchReturnTypeImpl is the implementation of SwitchReturnType
template <bool IS_CASTABLE, typename REQUESTED_TYPE, typename... CASE_RETURN_TYPES>
struct SwitchReturnTypeImpl;
/// SwitchReturnTypeImpl specialization for non-castable case types and an
/// explicitly specified return type.
template <typename REQUESTED_TYPE, typename... CASE_RETURN_TYPES>
struct SwitchReturnTypeImpl</*IS_CASTABLE*/ false, REQUESTED_TYPE, CASE_RETURN_TYPES...> {
/// Resolves to `REQUESTED_TYPE`
using type = REQUESTED_TYPE;
};
/// SwitchReturnTypeImpl specialization for non-castable case types and an
/// inferred return type.
template <typename... CASE_RETURN_TYPES>
struct SwitchReturnTypeImpl</*IS_CASTABLE*/ false, Infer, CASE_RETURN_TYPES...> {
/// Resolves to the common type for all the cases return types.
using type = std::common_type_t<CASE_RETURN_TYPES...>;
};
/// SwitchReturnTypeImpl specialization for castable case types and an
/// explicitly specified return type.
template <typename REQUESTED_TYPE, typename... CASE_RETURN_TYPES>
struct SwitchReturnTypeImpl</*IS_CASTABLE*/ true, REQUESTED_TYPE, CASE_RETURN_TYPES...> {
public:
/// Resolves to `const REQUESTED_TYPE*` or `REQUESTED_TYPE*`
using type = PropagateReturnConst<std::remove_pointer_t<REQUESTED_TYPE>, CASE_RETURN_TYPES...>*;
};
/// SwitchReturnTypeImpl specialization for castable case types and an infered
/// return type.
template <typename... CASE_RETURN_TYPES>
struct SwitchReturnTypeImpl</*IS_CASTABLE*/ true, Infer, CASE_RETURN_TYPES...> {
private:
using InferredType =
CastableCommonBase<detail::NullptrToIgnore<std::remove_pointer_t<CASE_RETURN_TYPES>>...>;
public:
/// `const T*` or `T*`, where T is the common base type for all the castable
/// case types.
using type = PropagateReturnConst<InferredType, CASE_RETURN_TYPES...>*;
};
/// Resolves to the return type for a Switch() with the requested return type
/// `REQUESTED_TYPE` and case statement return types. If `REQUESTED_TYPE` is
/// Infer then the return type will be inferred from the case return types.
template <typename REQUESTED_TYPE, typename... CASE_RETURN_TYPES>
using SwitchReturnType = typename SwitchReturnTypeImpl<
IsCastable<NullptrToIgnore<std::remove_pointer_t<CASE_RETURN_TYPES>>...>,
REQUESTED_TYPE,
CASE_RETURN_TYPES...>::type;
} // namespace detail
/// Switch is used to dispatch one of the provided callback case handler
/// functions based on the type of `object` and the parameter type of the case
/// handlers. Switch will sequentially check the type of `object` against each
/// of the switch case handler functions, and will invoke the first case handler
/// function which has a parameter type that matches the object type. When a
/// case handler is matched, it will be called with the single argument of
/// `object` cast to the case handler's parameter type. Switch will invoke at
/// most one case handler. Each of the case functions must have the signature
/// `R(T*)` or `R(const T*)`, where `T` is the type matched by that case and `R`
/// is the return type, consistent across all case handlers.
///
/// An optional default case function with the signature `R(Default)` can be
/// used as the last case. This default case will be called if all previous
/// cases failed to match.
///
/// If `object` is nullptr and a default case is provided, then the default case
/// will be called. If `object` is nullptr and no default case is provided, then
/// no cases will be called.
///
/// Example:
/// ```
/// Switch(object,
/// [&](TypeA*) { /* ... */ },
/// [&](TypeB*) { /* ... */ });
///
/// Switch(object,
/// [&](TypeA*) { /* ... */ },
/// [&](TypeB*) { /* ... */ },
/// [&](Default) { /* Called if object is not TypeA or TypeB */ });
/// ```
///
/// @param object the object who's type is used to
/// @param cases the switch cases
/// @return the value returned by the called case. If no cases matched, then the
/// zero value for the consistent case type.
template <typename RETURN_TYPE = detail::Infer, typename T = CastableBase, typename... CASES>
inline auto Switch(T* object, CASES&&... cases) {
using ReturnType = detail::SwitchReturnType<RETURN_TYPE, traits::ReturnType<CASES>...>;
static constexpr bool kHasReturnType = !std::is_same_v<ReturnType, void>;
if constexpr (kHasReturnType) {
// Replacement for std::aligned_storage as this is broken on earlier versions of MSVC.
struct alignas(alignof(ReturnType)) ReturnStorage {
uint8_t data[sizeof(ReturnType)];
};
ReturnStorage storage;
auto* res = utils::Bitcast<ReturnType*>(&storage);
TINT_DEFER(res->~ReturnType());
detail::SwitchCases(object, res, std::forward_as_tuple(std::forward<CASES>(cases)...));
return *res;
} else {
detail::SwitchCases<T, void>(object, nullptr,
std::forward_as_tuple(std::forward<CASES>(cases)...));
}
}
} // namespace tint
TINT_CASTABLE_POP_DISABLE_WARNINGS();
#endif // SRC_TINT_CASTABLE_H_