Boost logo

Boost :

Subject: Re: [boost] [interest] Type Aspects and compile-time constraints
From: Jeffrey Lee Hellrung, Jr. (jeffrey.hellrung_at_[hidden])
Date: 2012-02-09 01:54:18


On Wed, Feb 8, 2012 at 3:58 PM, Beren Minor <beren.minor+boost_at_[hidden]>wrote:

> > Looks to me like the traits idiom (e.g., std::iterator_traits). Perhaps a
> > concrete example will illustrate your point better?
>
> It's close to what std::iterator_traits defines but generalizes it.
> Let's get more practical and take my "Numeric" aspect I was talking
> about above.
>
> I want to describe number related types over a given number of
> properties. Let say that signness, integralness and width are
> properties that matter for numeric types.
> These aspect axises could be defined in a similar way by givin them an
> "Aspect" name, and a given set of values:
>
> META_ASPECT_DECLARE(sign, (signed_)(unsigned_)(any))
> META_ASPECT_DECLARE(integral, (floating)(fixed)(integral)(boolean)(any))
> META_ASPECT_DECLARE(width, (one_bit)(two_bits)(three_bits)
> [...] (eight_bytes)(any))
>
> This defines three aspects axises, sign, integral and width which
> could take the given values.
> This is a slightly simplified version of things I'm actually using.
> The detail of the macros isn't very relevant.
>
> Now let's define the aspect of a numerical type:
>
> template< typename Type, typename Sign, typename Integral, typename Width >
> struct numeric_type_aspect {
> typedef Type type;
> typedef Sign sign;
> typedef Integral integral;
> typedef Width width;
> };
>
> #define DECLARE_TYPE(type_m, sign_m, integral_m, width_m) \
> template< > struct numeric_type< type::type_m > { \
> typedef numeric_type_aspect< type::type_m, \
> sign::sign_m, \
> integral::integral_m, \
> width::width_m > aspect; \
> }; \
> typedef numeric_type< type::type_m > type_m;
>
> DECLARE_TYPE(signed_, signed_, any, any)
> DECLARE_TYPE(unsigned_, unsigned_, any, any)
>
> DECLARE_TYPE(integral_, any, integral, any)
> DECLARE_TYPE(signed_integral, signed_, integral, any)
>
> [...]
>
> DECLARE_TYPE(uint1_, unsigned_, integral, one_bit)
> DECLARE_TYPE(uint2_, unsigned_, integral, two_bits)
> DECLARE_TYPE(uint4_, unsigned_, integral, four_bits)
> DECLARE_TYPE(float_least10_, signed_, floating, eleven_bits)
>
> #undef DECLARE_TYPE
>
> This is purely abstract types for my internal constraint checking but
> this could be done more the same way when describing any type.
> Here I've defined a full list of numeric types, more or less defined
> (signed_ is a vaguely defined signed_ type, whereas uint4_ is a fully
> defined unsigned integral type on 4bits) that are described on a given
> set of axises (three plus one which is the actual type itself).
>
> The fact that the type itself is a description axis allows me to use
> it to describe the numeric aspect of some more general data type.
> For example the way OpenGL accepts data to load from host memory to
> the GPU textures can be defined using a numeric aspect, as long as
> with some other:
>
> META_ASPECT_DECLARE(packing, (one_in_one)(two_in_one)
> (three_in_one)(four_in_one))
> META_ASPECT_DECLARE(order, (forward)(reverse))
>
> template< typename Type, typename Numeric, typename Packing, typename
> Order >
> struct data_type_aspect {
> typedef Type type;
> typedef Numeric numeric;
> typedef Packing packing;
> typedef Order order;
> };
>
> #define DECLARE_TYPE(type_m, numeric_m, packing_m, order_m) \
> template< > struct data_type< type::type_m > { \
> typedef data_type_aspect< type::type_m, \
> fcmn::numeric::numeric_m, \
> fcmn::packing::packing_m, \
> fcmn::order::order_m > aspect; \
> }; \
> typedef data_type< type::type_m > type_m;
>
> DECLARE_TYPE(gl_unsigned_byte, uint8_, one_in_one, forward)
> DECLARE_TYPE(gl_byte, int8_, one_in_one, forward)
> DECLARE_TYPE(gl_unsigned_short, uint16_, one_in_one, forward)
> DECLARE_TYPE(gl_short, int16_, one_in_one, forward)
>
> [...]
>
> DECLARE_TYPE(gl_unsigned_int_24_8, ufloat32_, two_in_one, forward)
> DECLARE_TYPE(gl_unsigned_int_10f_11f_11f_rev, ufloat32_,
> three_in_one, reverse)
> DECLARE_TYPE(gl_unsigned_int_5_9_9_9_rev, ufloat32_,
> three_in_one, reverse)
> DECLARE_TYPE(gl_float_32_unsigned_int_24_8_rev, float32_stride32_,
> two_in_one, reverse)
>
> #undef DECLARE_TYPE
>
> The numeric aspect of every data type is one of the numeric type
> defined above, which has all its properties well defined. Then, the
> packing aspect can describe how colors components are packed in the
> source data (we can have a set of integers on 32bits but do we have
> one, two, three or four components packed in it?), then the order
> aspect describes how these components are ordered.
>
> Then constraints can be written by querying these aspects (which is
> done in a generalized way, and using boilerplate code generated by the
> macros) and using MPL to generate boolean conditions:
>
> template< typename DataType, typename GroupType >
> struct integral_check {
> typedef bm::and_< fnum::integral::is_integral<
> typename fcmn::get_numeric< GroupType >::type
> >,
> fnum::integral::is_not_integral<
> typename fcmn::get_numeric< DataType >::type
> > > group_is_integral_but_data_is_not;
> typedef bm::not_< group_is_integral_but_data_is_not > type;
>
> static_assert(type::value, "GroupType is not compatible with DataType");
> static_assert(type::value, "");
> static_assert(type::value, " [3.7.2 Transfer of Pixel Rectangles]");
> static_assert(type::value, " [3.9.3 Texture Image Specification]");
> static_assert(bm::not_< group_is_integral_but_data_is_not >::value,
> " - GroupType is integral but DataType is not.");
> };
>
> template< typename DataType, typename GroupType >
> struct component_check {
> typedef bm::and_< fcmn::component::is_depth_stencil< GroupType >,
> fcmn::packing::is_not_two_in_one< DataType >
> > group_is_depth_stencil_but_data_is_not_two_packed;
> typedef bm::not_< group_is_depth_stencil_but_data_is_not_two_packed >
> type;
>
> static_assert(type::value, "GroupType is not compatible with DataType");
> static_assert(type::value, "");
> static_assert(type::value, " [3.7.2 Transfer of Pixel Rectangles]");
> static_assert(type::value, " [3.9.3 Texture Image Specification]");
> static_assert(bm::not_<
> group_is_depth_stencil_but_data_is_not_two_packed
> >::value,
> " - GroupType is depth_stencil but DataType is not
> two_in_one packed.");
> };

I'm still not really getting this. :(

It *looks* to me like you're doing something equivalent to

namespace numeric_tags { /*...*/ }
namespace packing_tags { /*...*/ }
namespace order_tags { /*...*/ }

template< class T > struct gl_data_traits;

template<> struct gl_data_traits< gl_unsigned_byte >
{
    typedef numeric_tags::uint8_ numeric;
    typedef packing_tags::one_in_one packing;
    typedef order_tags::forward order;
};
template<> struct gl_data_traits< gl_byte > { /*...*/ };
/* ...etc... */

I would not consider anything that I typed above to be particularly novel;
it's basically the same pattern that, e.g., std::iterator_traits and
std::numeric_limits have. I don't see really any functional difference
between what I wrote and what you wrote. Clearly there's something "new"
about your design that I think I'm missing :(

- Jeff


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk