/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2018 Danny Robson */ #pragma once #include "../std.hpp" #include "../types.hpp" #include "../debug.hpp" #include #include #include #include namespace cruft::types { /////////////////////////////////////////////////////////////////////////// /// Computes the number of elements of a scalar (ie, 1), or of statically /// sized vector type template struct arity_trait { }; //------------------------------------------------------------------------- // void arity is explicitly zero. template <> struct arity_trait : public std::integer_sequence { }; //------------------------------------------------------------------------- // All basic types are unit arity (except for void which is zero). template struct arity_trait< T, std::enable_if_t> > : public std::integral_constant< std::size_t, 1u > { }; //------------------------------------------------------------------------- template constexpr auto arity_v = arity_trait::value; /////////////////////////////////////////////////////////////////////////// enum class category { NONE, UNSIGNED, SIGNED, REAL, }; ///------------------------------------------------------------------------ /// A description of the type that makes up a scalar or vector type. /// /// A vector3f would be { REAL, sizeof(float), 3 }; struct description { /// The signed, realness, or voidness of the type. enum category category; /// The number of bytes for an individual instance of this type. std::size_t width; /// The number of variables that make up an instance. std::size_t arity; std::size_t alignment; /// Returns the number of bytes required to store this type. constexpr std::size_t size (void) const noexcept { return width * arity; } }; //------------------------------------------------------------------------- constexpr bool operator== (description const &a, description const &b) noexcept { return a.category == b.category && a.width == b.width && a.arity == b.arity && a.alignment == b.alignment; } //------------------------------------------------------------------------- template struct category_traits; template <> struct category_traits : public std::integral_constant {}; template <> struct category_traits : public std::integral_constant {}; template <> struct category_traits : public std::integral_constant {}; template <> struct category_traits : public std::integral_constant {}; template <> struct category_traits : public std::integral_constant {}; template <> struct category_traits : public std::integral_constant {}; template <> struct category_traits : public std::integral_constant {}; template <> struct category_traits : public std::integral_constant {}; template <> struct category_traits : public std::integral_constant {}; template <> struct category_traits : public std::integral_constant {}; template constexpr auto category_traits_v = category_traits::value; //------------------------------------------------------------------------- template constexpr description make_description (void) noexcept { if constexpr (std::is_void_v) { return { .category = category::NONE, .width = 0, .arity = 1, .alignment = 0, }; } else { return { .category = category_traits_v, .width = sizeof (T), .arity = arity_v, .alignment = alignof (T) }; } } /// Call a functor with the supplied arguments and a type_tag for the /// described native type. /// /// If the type does not exist then throw and invalid_argument exception. template decltype(auto) visit (description const &descriminator, FunctionT &&func, Args&&...args) { switch (descriminator.category) { case category ::NONE: return std::invoke ( std::forward (func), std::forward (args)..., type_tag {} ); case category::REAL: switch (descriminator.width) { case 4: return std::invoke ( std::forward (func), std::forward (args)..., type_tag {} ); case 8: return std::invoke ( std::forward (func), std::forward (args)..., type_tag {} ); default: throw std::invalid_argument ("Unsupported floating point width"); } case category::SIGNED: switch (descriminator.width) { case 1: return std::invoke ( std::forward (func), std::forward (args)..., type_tag {} ); case 2: return std::invoke ( std::forward (func), std::forward (args)..., type_tag {} ); case 4: return std::invoke ( std::forward (func), std::forward (args)..., type_tag {} ); case 8: return std::invoke ( std::forward (func), std::forward (args)..., type_tag {} ); default: throw std::invalid_argument ("Unsupported unsigned width"); } case category::UNSIGNED: switch (descriminator.width) { case 1: return std::invoke ( std::forward (func), std::forward (args)..., type_tag {} ); case 2: return std::invoke ( std::forward (func), std::forward (args)..., type_tag {} ); case 4: return std::invoke ( std::forward (func), std::forward (args)..., type_tag {} ); case 8: return std::invoke ( std::forward (func), std::forward (args)..., type_tag {} ); } } unhandled (descriminator.category); } } #include namespace cruft::types { std::ostream& operator<< (std::ostream&, category); std::ostream& operator<< (std::ostream&, description); }