types/description: add initial enum support

This commit is contained in:
Danny Robson 2019-05-30 10:43:28 +10:00
parent d3f23ed754
commit 41edd3cebe
8 changed files with 282 additions and 134 deletions

View File

@ -486,6 +486,7 @@ list (
types/comparator.hpp types/comparator.hpp
types/description.cpp types/description.cpp
types/description.hpp types/description.hpp
types/dispatch.hpp
types/string.cpp types/string.cpp
types/string.hpp types/string.hpp
types/tagged.hpp types/tagged.hpp

View File

@ -257,4 +257,15 @@ namespace cruft::types {
> : public category_traits< > : public category_traits<
::cruft::value_trait_t<CoordT> ::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>
> { };
} }

View File

@ -137,8 +137,7 @@ namespace cruft::types {
template <typename TagT, typename ValueT> template <typename TagT, typename ValueT>
struct arity_trait< struct arity_trait<
::cruft::strongdef::index<TagT, ValueT> ::cruft::strongdef::index<TagT, ValueT>
> : > : public arity_trait<ValueT>
arity_trait<ValueT>
{ ; }; { ; };
@ -148,4 +147,11 @@ namespace cruft::types {
> : > :
public category_traits<ValueT> public category_traits<ValueT>
{}; {};
}
template <typename TagT, typename ValueT>
struct signedness_traits<
::cruft::strongdef::index<TagT,ValueT>
> : public signedness_traits<ValueT>
{ ; };
};

View File

@ -1,5 +1,28 @@
#include "tap.hpp" #include "tap.hpp"
#include "types/description.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 ( tap.expect_eq (
cruft::types::make_description<int> (), cruft::types::make_description<int> (),
cruft::types::description { cruft::types::description {
.category = cruft::types::category::SIGNED, .category = cruft::types::category::INTEGER,
.width = sizeof (int), .signedness = true,
.arity = 1, .width = sizeof (int),
.alignment = alignof (int) .arity = 1,
.alignment = alignof (int),
.index = cruft::typeidx<int> (),
}, },
"description: 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 (); return tap.status ();
} }

View File

@ -8,6 +8,8 @@
#pragma once #pragma once
#include "types/description.hpp"
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <memory> #include <memory>
@ -70,7 +72,36 @@ first (T a, Args&& ...b)
namespace cruft { 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 /// represents a type as a POD struct (but is statically recoverable via
/// the 'type' member). /// the 'type' member).
template <typename T> 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 /// count the number of parameters we are given. useful for counting
/// arguments in variadic macros (ie, sizeof... (__VA_ARGS__)) /// arguments in variadic macros (ie, sizeof... (__VA_ARGS__))

View File

@ -17,10 +17,10 @@ cruft::types::operator<< (std::ostream &os, cruft::types::category val)
{ {
switch (val) { switch (val) {
case category::NONE: return os << "NONE"; case category::NONE: return os << "NONE";
case category::UNSIGNED: return os << "UNSIGNED"; case category::INTEGER: return os << "INTEGER";
case category::SIGNED: return os << "SIGNED";
case category::REAL: return os << "REAL"; case category::REAL: return os << "REAL";
case category::BOOL: return os << "BOOL"; case category::BOOL: return os << "BOOL";
case category::ENUM: return os << "ENUM";
} }
unreachable (); unreachable ();
@ -31,10 +31,12 @@ cruft::types::operator<< (std::ostream &os, cruft::types::category val)
std::ostream& std::ostream&
cruft::types::operator<< (std::ostream &os, cruft::types::description val) cruft::types::operator<< (std::ostream &os, cruft::types::description val)
{ {
return os << "{ category: " << val.category return os << "{ category: " << val.category
<< ", width: " << val.width << ", signedness: " << val.signedness
<< ", arity: " << val.arity << ", width: " << val.width
<< ", alignment: " << val.alignment << ", arity: " << val.arity
<< ", alignment: " << val.alignment
<< ", index: " << val.index
<< " }"; << " }";
} }
@ -58,10 +60,10 @@ cruft::debug::validator<cruft::types::description>::is_valid (
switch (val.category) { switch (val.category) {
// Ensure types that look fundamental might have a chance of being // Ensure types that look fundamental might have a chance of being
// represented in hardware. // represented in hardware.
case cruft::types::category::UNSIGNED: case cruft::types::category::INTEGER:
case cruft::types::category::SIGNED:
case cruft::types::category::REAL: case cruft::types::category::REAL:
case cruft::types::category::BOOL: case cruft::types::category::BOOL:
case cruft::types::category::ENUM:
// Most numbers are only realistic if they're powers of two; it's not // Most numbers are only realistic if they're powers of two; it's not
// a requirement, it's just stupid suspicious. // a requirement, it's just stupid suspicious.
// //

View File

@ -3,14 +3,14 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. * 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 #pragma once
#include "../std.hpp" #include "../std.hpp"
#include "../types.hpp"
#include "../debug/panic.hpp" #include "../debug/panic.hpp"
#include "../typeidx.hpp"
#include <cstddef> #include <cstddef>
#include <type_traits> #include <type_traits>
@ -62,10 +62,10 @@ namespace cruft::types {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
enum class category { enum class category {
NONE, NONE,
UNSIGNED, INTEGER,
SIGNED,
REAL, REAL,
BOOL, BOOL,
ENUM,
}; };
@ -78,9 +78,18 @@ namespace cruft::types {
/// that confuses total byte size, element byte size, and arity. /// that confuses total byte size, element byte size, and arity.
/// Instead: use the bytes member function, or the arity member variable. /// Instead: use the bytes member function, or the arity member variable.
struct description { 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; 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. /// The number of bytes for an individual instance of this type.
std::size_t width; std::size_t width;
@ -89,6 +98,10 @@ namespace cruft::types {
std::size_t alignment; 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. /// Returns the number of bytes required to store this type.
constexpr std::size_t constexpr std::size_t
bytes (void) const noexcept bytes (void) const noexcept
@ -102,10 +115,12 @@ namespace cruft::types {
constexpr bool constexpr bool
operator== (description const &a, description const &b) noexcept operator== (description const &a, description const &b) noexcept
{ {
return a.category == b.category && return a.category == b.category &&
a.width == b.width && a.signedness == b.signedness &&
a.arity == b.arity && a.width == b.width &&
a.alignment == b.alignment; 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> template <typename T, typename = void>
struct category_traits; struct category_traits;
template <> struct category_traits<u08> : 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::UNSIGNED> {}; template <> struct category_traits<u16> : public std::integral_constant<category,category::INTEGER> {};
template <> struct category_traits<u32> : public std::integral_constant<category,category::UNSIGNED> {}; template <> struct category_traits<u32> : public std::integral_constant<category,category::INTEGER> {};
template <> struct category_traits<u64> : public std::integral_constant<category,category::UNSIGNED> {}; 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<i08> : public std::integral_constant<category,category::INTEGER> {};
template <> struct category_traits<i16> : public std::integral_constant<category,category::SIGNED> {}; template <> struct category_traits<i16> : public std::integral_constant<category,category::INTEGER> {};
template <> struct category_traits<i32> : public std::integral_constant<category,category::SIGNED> {}; template <> struct category_traits<i32> : public std::integral_constant<category,category::INTEGER> {};
template <> struct category_traits<i64> : public std::integral_constant<category,category::SIGNED> {}; 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<f32> : public std::integral_constant<category,category::REAL> {};
template <> struct category_traits<f64> : 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::enable_if_t<
std::is_enum_v<EnumT> 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> template <typename T>
constexpr description constexpr auto signedness_traits_v = signedness_traits<T>::value;
//-------------------------------------------------------------------------
template <typename T>
description
make_description (void) noexcept make_description (void) noexcept
{ {
if constexpr (std::is_void_v<T>) { if constexpr (std::is_void_v<T>) {
return { return {
.category = category::NONE, .category = category::NONE,
.width = 0, .signedness = false,
.arity = 1, .width = 0,
.alignment = 0, .arity = 1,
.alignment = 0,
.index = cruft::typeidx<T> (),
}; };
} else { } else {
return { return {
.category = category_traits_v<T>, .category = category_traits_v<T>,
.signedness = signedness_traits_v<T>,
.width = sizeof (T), .width = sizeof (T),
.arity = arity_v<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> #include <iosfwd>
namespace cruft::types { namespace cruft::types {
std::ostream& operator<< (std::ostream&, category); std::ostream& operator<< (std::ostream&, category);

89
types/dispatch.hpp Normal file
View 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
}
}