267 lines
8.8 KiB
C++
267 lines
8.8 KiB
C++
/*
|
|
* 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 <danny@nerdcruft.net>
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "../std.hpp"
|
|
#include "../types.hpp"
|
|
#include "../debug/panic.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 <typename T>
|
|
constexpr auto arity_v = arity_trait<T>::value;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
enum class category {
|
|
NONE,
|
|
UNSIGNED,
|
|
SIGNED,
|
|
REAL,
|
|
BOOL,
|
|
};
|
|
|
|
|
|
///------------------------------------------------------------------------
|
|
/// 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;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
constexpr bool
|
|
operator!= (description const &a, description const &b) noexcept
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <typename T, typename = void>
|
|
struct category_traits;
|
|
|
|
template <> struct category_traits<u08> : public std::integral_constant<category,category::UNSIGNED> {};
|
|
template <> struct category_traits<u16> : public std::integral_constant<category,category::UNSIGNED> {};
|
|
template <> struct category_traits<u32> : public std::integral_constant<category,category::UNSIGNED> {};
|
|
template <> struct category_traits<u64> : public std::integral_constant<category,category::UNSIGNED> {};
|
|
|
|
template <> struct category_traits<i08> : public std::integral_constant<category,category::SIGNED> {};
|
|
template <> struct category_traits<i16> : public std::integral_constant<category,category::SIGNED> {};
|
|
template <> struct category_traits<i32> : public std::integral_constant<category,category::SIGNED> {};
|
|
template <> struct category_traits<i64> : public std::integral_constant<category,category::SIGNED> {};
|
|
|
|
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 category_traits<std::underlying_type_t<EnumT>>
|
|
{ };
|
|
|
|
|
|
template <typename T>
|
|
constexpr auto category_traits_v = category_traits<T>::value;
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <typename T>
|
|
constexpr description
|
|
make_description (void) noexcept
|
|
{
|
|
if constexpr (std::is_void_v<T>) {
|
|
return {
|
|
.category = category::NONE,
|
|
.width = 0,
|
|
.arity = 1,
|
|
.alignment = 0,
|
|
};
|
|
} else {
|
|
return {
|
|
.category = category_traits_v<T>,
|
|
.width = sizeof (T),
|
|
.arity = arity_v<T>,
|
|
.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 <typename FunctionT, typename ...Args>
|
|
decltype(auto)
|
|
visit (description const &descriminator, FunctionT &&func, Args&&...args)
|
|
{
|
|
switch (descriminator.category) {
|
|
case category ::NONE:
|
|
return std::invoke (
|
|
std::forward<FunctionT> (func),
|
|
std::forward<Args> (args)...,
|
|
type_tag<void> {}
|
|
);
|
|
|
|
case category::REAL:
|
|
switch (descriminator.width) {
|
|
case 4: return std::invoke (
|
|
std::forward<FunctionT> (func),
|
|
std::forward<Args> (args)...,
|
|
type_tag<f32> {}
|
|
);
|
|
case 8: return std::invoke (
|
|
std::forward<FunctionT> (func),
|
|
std::forward<Args> (args)...,
|
|
type_tag<f64> {}
|
|
);
|
|
default:
|
|
throw std::invalid_argument ("Unsupported floating point width");
|
|
}
|
|
|
|
case category::SIGNED:
|
|
switch (descriminator.width) {
|
|
case 1: return std::invoke (
|
|
std::forward<FunctionT> (func),
|
|
std::forward<Args> (args)...,
|
|
type_tag<i08> {}
|
|
);
|
|
case 2: return std::invoke (
|
|
std::forward<FunctionT> (func),
|
|
std::forward<Args> (args)...,
|
|
type_tag<i16> {}
|
|
);
|
|
case 4: return std::invoke (
|
|
std::forward<FunctionT> (func),
|
|
std::forward<Args> (args)...,
|
|
type_tag<i32> {}
|
|
);
|
|
case 8: return std::invoke (
|
|
std::forward<FunctionT> (func),
|
|
std::forward<Args> (args)...,
|
|
type_tag<i64> {}
|
|
);
|
|
default:
|
|
throw std::invalid_argument ("Unsupported unsigned width");
|
|
}
|
|
|
|
case category::UNSIGNED:
|
|
switch (descriminator.width) {
|
|
case 1: return std::invoke (
|
|
std::forward<FunctionT> (func),
|
|
std::forward<Args> (args)...,
|
|
type_tag<u08> {}
|
|
);
|
|
case 2: return std::invoke (
|
|
std::forward<FunctionT> (func),
|
|
std::forward<Args> (args)...,
|
|
type_tag<u16> {}
|
|
);
|
|
case 4: return std::invoke (
|
|
std::forward<FunctionT> (func),
|
|
std::forward<Args> (args)...,
|
|
type_tag<u32> {}
|
|
);
|
|
case 8: return std::invoke (
|
|
std::forward<FunctionT> (func),
|
|
std::forward<Args> (args)...,
|
|
type_tag<u64> {}
|
|
);
|
|
}
|
|
|
|
case category::BOOL:
|
|
return std::invoke (
|
|
std::forward<FunctionT> (func),
|
|
std::forward<Args> (args)...,
|
|
type_tag<bool> {}
|
|
);
|
|
}
|
|
|
|
unhandled (descriminator.category);
|
|
}
|
|
}
|
|
|
|
#include <iosfwd>
|
|
namespace cruft::types {
|
|
std::ostream& operator<< (std::ostream&, category);
|
|
std::ostream& operator<< (std::ostream&, description);
|
|
} |