types/description: add initial enum support
This commit is contained in:
parent
d3f23ed754
commit
41edd3cebe
@ -486,6 +486,7 @@ list (
|
||||
types/comparator.hpp
|
||||
types/description.cpp
|
||||
types/description.hpp
|
||||
types/dispatch.hpp
|
||||
types/string.cpp
|
||||
types/string.hpp
|
||||
types/tagged.hpp
|
||||
|
@ -257,4 +257,15 @@ namespace cruft::types {
|
||||
> : public category_traits<
|
||||
::cruft::value_trait_t<CoordT>
|
||||
> { };
|
||||
|
||||
|
||||
template <typename CoordT>
|
||||
struct signedness_traits<
|
||||
CoordT,
|
||||
std::enable_if_t<
|
||||
::cruft::is_coord_v<CoordT>
|
||||
>
|
||||
> : public signedness_traits<
|
||||
::cruft::value_trait_t<CoordT>
|
||||
> { };
|
||||
}
|
||||
|
@ -137,8 +137,7 @@ namespace cruft::types {
|
||||
template <typename TagT, typename ValueT>
|
||||
struct arity_trait<
|
||||
::cruft::strongdef::index<TagT, ValueT>
|
||||
> :
|
||||
arity_trait<ValueT>
|
||||
> : public arity_trait<ValueT>
|
||||
{ ; };
|
||||
|
||||
|
||||
@ -148,4 +147,11 @@ namespace cruft::types {
|
||||
> :
|
||||
public category_traits<ValueT>
|
||||
{};
|
||||
}
|
||||
|
||||
|
||||
template <typename TagT, typename ValueT>
|
||||
struct signedness_traits<
|
||||
::cruft::strongdef::index<TagT,ValueT>
|
||||
> : public signedness_traits<ValueT>
|
||||
{ ; };
|
||||
};
|
||||
|
@ -1,5 +1,28 @@
|
||||
#include "tap.hpp"
|
||||
#include "types/description.hpp"
|
||||
#include "types/dispatch.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
enum an_enum_type { AN_ENUM_VALUE };
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct does_enum_match {
|
||||
template <typename TagT>
|
||||
bool operator() (cruft::type_tag<TagT> const&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename UnderlyingT>
|
||||
bool operator() (cruft::type_tag<cruft::unknown_enum_tag<UnderlyingT>> const &tag)
|
||||
{
|
||||
auto const expected = cruft::types::make_description<an_enum_type> ();
|
||||
return expected == tag.desc;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -10,13 +33,34 @@ int main ()
|
||||
tap.expect_eq (
|
||||
cruft::types::make_description<int> (),
|
||||
cruft::types::description {
|
||||
.category = cruft::types::category::SIGNED,
|
||||
.width = sizeof (int),
|
||||
.arity = 1,
|
||||
.alignment = alignof (int)
|
||||
.category = cruft::types::category::INTEGER,
|
||||
.signedness = true,
|
||||
.width = sizeof (int),
|
||||
.arity = 1,
|
||||
.alignment = alignof (int),
|
||||
.index = cruft::typeidx<int> (),
|
||||
},
|
||||
"description: int"
|
||||
);
|
||||
|
||||
tap.expect_neq (
|
||||
cruft::types::make_description<i16> (),
|
||||
cruft::types::make_description<u16> (),
|
||||
"different signedness is unequal"
|
||||
);
|
||||
|
||||
tap.expect_eq (
|
||||
cruft::types::make_description<an_enum_type> ().category,
|
||||
cruft::types::category::ENUM,
|
||||
"enums are described as such"
|
||||
);
|
||||
|
||||
|
||||
auto const enum_type_descriptor = cruft::types::make_description<an_enum_type> ();
|
||||
tap.expect (
|
||||
cruft::types::visit (enum_type_descriptor, does_enum_match {}),
|
||||
"enum type descriptor dispatches correctly"
|
||||
);
|
||||
|
||||
return tap.status ();
|
||||
}
|
40
types.hpp
40
types.hpp
@ -8,6 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types/description.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
@ -70,7 +72,36 @@ first (T a, Args&& ...b)
|
||||
|
||||
|
||||
namespace cruft {
|
||||
///------------------------------------------------------------------------
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// A tag type that is used when type_tag dispatching descriptions that
|
||||
/// claim to be enums. The underlying type can be recovered from the 'type'
|
||||
/// typedef of this class.
|
||||
template <typename UnderlyingT>
|
||||
struct unknown_enum_tag {
|
||||
using type = UnderlyingT;
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
template <typename ValueT>
|
||||
struct is_unknown_enum_tag :
|
||||
public std::false_type {};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
template <
|
||||
typename UnderlyingT
|
||||
> struct is_unknown_enum_tag<
|
||||
unknown_enum_tag<UnderlyingT>
|
||||
>: public std::true_type {};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
template <typename ValueT>
|
||||
constexpr auto is_unknown_enum_tag_v = is_unknown_enum_tag<ValueT>::value;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// represents a type as a POD struct (but is statically recoverable via
|
||||
/// the 'type' member).
|
||||
template <typename T>
|
||||
@ -80,6 +111,13 @@ namespace cruft {
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
template <typename UnderlyingT>
|
||||
struct type_tag<unknown_enum_tag<UnderlyingT>> {
|
||||
cruft::types::description desc;
|
||||
};
|
||||
|
||||
|
||||
///------------------------------------------------------------------------
|
||||
/// count the number of parameters we are given. useful for counting
|
||||
/// arguments in variadic macros (ie, sizeof... (__VA_ARGS__))
|
||||
|
@ -17,10 +17,10 @@ cruft::types::operator<< (std::ostream &os, cruft::types::category val)
|
||||
{
|
||||
switch (val) {
|
||||
case category::NONE: return os << "NONE";
|
||||
case category::UNSIGNED: return os << "UNSIGNED";
|
||||
case category::SIGNED: return os << "SIGNED";
|
||||
case category::INTEGER: return os << "INTEGER";
|
||||
case category::REAL: return os << "REAL";
|
||||
case category::BOOL: return os << "BOOL";
|
||||
case category::ENUM: return os << "ENUM";
|
||||
}
|
||||
|
||||
unreachable ();
|
||||
@ -31,10 +31,12 @@ cruft::types::operator<< (std::ostream &os, cruft::types::category val)
|
||||
std::ostream&
|
||||
cruft::types::operator<< (std::ostream &os, cruft::types::description val)
|
||||
{
|
||||
return os << "{ category: " << val.category
|
||||
<< ", width: " << val.width
|
||||
<< ", arity: " << val.arity
|
||||
<< ", alignment: " << val.alignment
|
||||
return os << "{ category: " << val.category
|
||||
<< ", signedness: " << val.signedness
|
||||
<< ", width: " << val.width
|
||||
<< ", arity: " << val.arity
|
||||
<< ", alignment: " << val.alignment
|
||||
<< ", index: " << val.index
|
||||
<< " }";
|
||||
}
|
||||
|
||||
@ -58,10 +60,10 @@ cruft::debug::validator<cruft::types::description>::is_valid (
|
||||
switch (val.category) {
|
||||
// Ensure types that look fundamental might have a chance of being
|
||||
// represented in hardware.
|
||||
case cruft::types::category::UNSIGNED:
|
||||
case cruft::types::category::SIGNED:
|
||||
case cruft::types::category::INTEGER:
|
||||
case cruft::types::category::REAL:
|
||||
case cruft::types::category::BOOL:
|
||||
case cruft::types::category::ENUM:
|
||||
// Most numbers are only realistic if they're powers of two; it's not
|
||||
// a requirement, it's just stupid suspicious.
|
||||
//
|
||||
|
@ -3,14 +3,14 @@
|
||||
* 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>
|
||||
* Copyright 2018-2019 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../std.hpp"
|
||||
#include "../types.hpp"
|
||||
#include "../debug/panic.hpp"
|
||||
#include "../typeidx.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
@ -62,10 +62,10 @@ namespace cruft::types {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
enum class category {
|
||||
NONE,
|
||||
UNSIGNED,
|
||||
SIGNED,
|
||||
INTEGER,
|
||||
REAL,
|
||||
BOOL,
|
||||
ENUM,
|
||||
};
|
||||
|
||||
|
||||
@ -78,9 +78,18 @@ namespace cruft::types {
|
||||
/// that confuses total byte size, element byte size, and arity.
|
||||
/// Instead: use the bytes member function, or the arity member variable.
|
||||
struct description {
|
||||
/// The signed, realness, or voidness of the type.
|
||||
/// 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;
|
||||
|
||||
@ -89,6 +98,10 @@ namespace cruft::types {
|
||||
|
||||
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
|
||||
@ -102,10 +115,12 @@ namespace cruft::types {
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@ -121,15 +136,15 @@ namespace cruft::types {
|
||||
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<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::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<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> {};
|
||||
@ -143,7 +158,7 @@ namespace cruft::types {
|
||||
std::enable_if_t<
|
||||
std::is_enum_v<EnumT>
|
||||
>
|
||||
> : public category_traits<std::underlying_type_t<EnumT>>
|
||||
> : public std::integral_constant<category,category::ENUM>
|
||||
{ };
|
||||
|
||||
|
||||
@ -152,122 +167,64 @@ namespace cruft::types {
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
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 description
|
||||
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,
|
||||
.width = 0,
|
||||
.arity = 1,
|
||||
.alignment = 0,
|
||||
.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)
|
||||
.alignment = alignof (T),
|
||||
.index = cruft::typeidx<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);
|
||||
|
89
types/dispatch.hpp
Normal file
89
types/dispatch.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
#include "description.hpp"
|
||||
#include "../types.hpp"
|
||||
|
||||
|
||||
namespace cruft::types {
|
||||
/// 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.
|
||||
///
|
||||
/// We split the calls into two here so that there's no possibility of
|
||||
/// recursion (which will blow up the compiler) when we handle the
|
||||
/// dispatch of enums.
|
||||
template <typename FunctionT, typename ...Args>
|
||||
decltype(auto)
|
||||
visit (description const &descriminator, FunctionT &&func, Args&&...args)
|
||||
{
|
||||
#define INVOKE(KLASS) \
|
||||
return std::invoke( \
|
||||
std::forward<FunctionT> (func), \
|
||||
std::forward<Args> (args)..., \
|
||||
type_tag<KLASS> {} \
|
||||
);
|
||||
|
||||
switch (descriminator.category) {
|
||||
case category ::NONE: INVOKE(void)
|
||||
case category::REAL:
|
||||
switch (descriminator.width) {
|
||||
case 4: INVOKE(f32)
|
||||
case 8: INVOKE(f64)
|
||||
default:
|
||||
throw std::invalid_argument ("Unsupported floating point width");
|
||||
}
|
||||
|
||||
case category::BOOL: INVOKE(bool)
|
||||
|
||||
case category::ENUM: {
|
||||
if (descriminator.signedness) {
|
||||
switch (descriminator.width) {
|
||||
case 1: INVOKE(unknown_enum_tag<i08>)
|
||||
case 2: INVOKE(unknown_enum_tag<i16>)
|
||||
case 3: INVOKE(unknown_enum_tag<i32>)
|
||||
case 4: INVOKE(unknown_enum_tag<i64>)
|
||||
}
|
||||
} else {
|
||||
switch (descriminator.width) {
|
||||
case 1: INVOKE(unknown_enum_tag<u08>)
|
||||
case 2: INVOKE(unknown_enum_tag<u16>)
|
||||
case 3: INVOKE(unknown_enum_tag<u32>)
|
||||
case 4: INVOKE(unknown_enum_tag<u64>)
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case category::INTEGER:
|
||||
CHECK_EQ (category::INTEGER, descriminator.category);
|
||||
|
||||
if (descriminator.signedness) {
|
||||
switch (descriminator.width) {
|
||||
case 1: INVOKE(i08)
|
||||
case 2: INVOKE(i16)
|
||||
case 3: INVOKE(i32)
|
||||
case 4: INVOKE(i64)
|
||||
}
|
||||
} else {
|
||||
switch (descriminator.width) {
|
||||
case 1: INVOKE(u08)
|
||||
case 2: INVOKE(u16)
|
||||
case 3: INVOKE(u32)
|
||||
case 4: INVOKE(u64)
|
||||
}
|
||||
}
|
||||
|
||||
unhandled (descriminator.category);
|
||||
}
|
||||
|
||||
unhandled (descriminator.category);
|
||||
#undef INVOKE
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user