
On Wed, Feb 8, 2012 at 3:58 PM, Beren Minor <beren.minor+boost@gmail.com>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