/* * 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-2019 Danny Robson <danny@nerdcruft.net> */ #pragma once #include "../std.hpp" #include "../debug/panic.hpp" #include "../typeidx.hpp" #include <cstddef> #include <type_traits> #include <functional> #include <utility> namespace cruft::types { /////////////////////////////////////////////////////////////////////////// /// Computes the number of elements of a scalar (ie, 1), or of statically /// sized vector type template <typename T, typename = void> struct arity_trait { }; //------------------------------------------------------------------------- // void arity is explicitly zero. template <> struct arity_trait<void, void> : public std::integer_sequence<std::size_t, 0> { }; //------------------------------------------------------------------------- // All basic types are unit arity (except for void which is zero). template <typename T> struct arity_trait< T, std::enable_if_t<std::is_fundamental_v<T>> > : public std::integral_constant< std::size_t, 1u > { }; //------------------------------------------------------------------------- // Punt the handling of any enum to the underlying type. template <typename EnumT> struct arity_trait< EnumT, std::enable_if_t<std::is_enum_v<EnumT>> > : public arity_trait< std::underlying_type_t<EnumT> > { }; //------------------------------------------------------------------------- template <std::size_t S, typename T> struct arity_trait< std::array<T,S> > : public std::integral_constant<std::size_t, S> { }; //------------------------------------------------------------------------- template <typename T> constexpr auto arity_v = arity_trait<T>::value; /////////////////////////////////////////////////////////////////////////// enum class category { NONE, INTEGER, REAL, BOOL, ENUM, }; ///------------------------------------------------------------------------ /// A description of the type that makes up a scalar or vector type. /// /// A vector3f would be { REAL, sizeof(float), 3 }; /// /// DO NOT add a 'size' member function as it tends to be used in a way /// that confuses total byte size, element byte size, and arity. /// Instead: use the bytes member function, or the arity member variable. struct description { /// The fundamental nature of the type; is it integral, real, bool, etc. enum category category; /// Is the type considered to be signed? /// /// For some types, like void, this does not make sense. In this case /// the value must be false. /// /// For other types this value will almost certainly be one value in a /// realistic system; eg, signed floating point numbers. bool signedness; /// 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; /// The value from typeidx for the constructed type (if it is known /// and has been registered), otherwise 0. int index; /// Returns the number of bytes required to store this type. constexpr std::size_t bytes (void) const noexcept { return width * arity; } }; //------------------------------------------------------------------------- constexpr bool operator== (description const &a, description const &b) noexcept { return a.category == b.category && a.signedness == b.signedness && a.width == b.width && a.arity == b.arity && a.alignment == b.alignment && (a.index == 0 || b.index == 0 || a.index == b.index); } //------------------------------------------------------------------------- constexpr bool operator!= (description const &a, description const &b) noexcept { return !(a == b); } /// A comparator that compares values as equal if they are an enum and the /// underlying_type of said enum. /// /// This is distinct from the raw equality operator so as to not pessimise /// the usability of the equality operator (for tasks that required strict /// equality). struct underlying_comparator { bool operator() ( description const &a, description const &b ) const noexcept { // If one of the descriptions is ENUM then both need to be either ENUM // or INTEGER (because we try to compare enums and their underlying // types as equal). if (a.category == category::ENUM || b.category == category::ENUM) { if (a.category != category::ENUM && a.category != category::INTEGER) return false; if (b.category != category::ENUM && b.category != category::INTEGER) return false; // Indices only need to match if they're both an ENUM, // otherwise we're testing the underlying type. if (a.category == category::ENUM && b.category == category::ENUM) if (a.index != b.index) return false; } else { if (a.index != b.index) return false; } // All details other must be identical; return a.signedness == b.signedness && a.width == b.width && a.arity == b.arity && a.alignment == b.alignment; }; }; //------------------------------------------------------------------------- template <typename T, typename = void> struct category_traits; template <> struct category_traits<u08> : public std::integral_constant<category,category::INTEGER> {}; template <> struct category_traits<u16> : public std::integral_constant<category,category::INTEGER> {}; template <> struct category_traits<u32> : public std::integral_constant<category,category::INTEGER> {}; template <> struct category_traits<u64> : public std::integral_constant<category,category::INTEGER> {}; template <> struct category_traits<i08> : public std::integral_constant<category,category::INTEGER> {}; template <> struct category_traits<i16> : public std::integral_constant<category,category::INTEGER> {}; template <> struct category_traits<i32> : public std::integral_constant<category,category::INTEGER> {}; template <> struct category_traits<i64> : public std::integral_constant<category,category::INTEGER> {}; template <> struct category_traits<f32> : public std::integral_constant<category,category::REAL> {}; template <> struct category_traits<f64> : public std::integral_constant<category,category::REAL> {}; template <> struct category_traits<bool> : public std::integral_constant<category,category::BOOL> {}; template <typename EnumT> struct category_traits< EnumT, std::enable_if_t< std::is_enum_v<EnumT> > > : public std::integral_constant<category,category::ENUM> { }; template <typename T> constexpr auto category_traits_v = category_traits<T>::value; //------------------------------------------------------------------------- template <typename, typename = void> struct signedness_traits; template <> struct signedness_traits<u08> : public std::false_type {}; template <> struct signedness_traits<u16> : public std::false_type {}; template <> struct signedness_traits<u32> : public std::false_type {}; template <> struct signedness_traits<u64> : public std::false_type {}; template <> struct signedness_traits<i08> : public std::true_type {}; template <> struct signedness_traits<i16> : public std::true_type {}; template <> struct signedness_traits<i32> : public std::true_type {}; template <> struct signedness_traits<i64> : public std::true_type {}; template <> struct signedness_traits<f32> : public std::true_type {}; template <> struct signedness_traits<f64> : public std::true_type {}; template <> struct signedness_traits<bool> : public std::false_type {}; template <typename EnumT> struct signedness_traits< EnumT, std::enable_if_t< std::is_enum_v<EnumT> > >: public signedness_traits<std::underlying_type_t<EnumT>> {}; template <typename T> constexpr auto signedness_traits_v = signedness_traits<T>::value; //------------------------------------------------------------------------- template <typename T> description make_description (void) noexcept { if constexpr (std::is_void_v<T>) { return { .category = category::NONE, .signedness = false, .width = 0, .arity = 1, .alignment = 0, .index = cruft::typeidx<T> (), }; } else { return { .category = category_traits_v<T>, .signedness = signedness_traits_v<T>, .width = sizeof (T), .arity = arity_v<T>, .alignment = alignof (T), .index = cruft::typeidx<T> (), }; } } } #include <iosfwd> namespace cruft::types { std::ostream& operator<< (std::ostream&, category); std::ostream& operator<< (std::ostream&, description); }