Select-By-Size

Metaprogramming
Utilities

Contents

1. Overview
2. Example
3. Description
4. Synopsis
5. Rationale
6. Implementation
7. Dependencies
8. Portability
9. Feedback

Overview

The header <boost/utility/select_by_size.hpp> provides template classes and macros for determining the results of overload resolution at compile time using sizeof. It is intended as an alternative to type_traits::yes_type and type_traits::no_type from the Type Traits library. It provides an arbitrary number of types, case_<0>, case_<1>, case_<2>, ... , for use as the return types of helper functions, plus a template select_by_size which provides access to the result of the overload resolution. There is also a macro BOOST_SELECT_BY_SIZE, similar to BOOST_STATIC_CONSTANT, for use with compilers which have trouble with integral constant expressions. (See Coding Guidelines for Integral Constant Expressions.)

The typedefs yes_type and no_type are provided as shorthands for case_<1> and case_<0>. In some cases, case_<true> and case_<false> may be more suggestive. There is also a single return type representing a negative value: case_<-1>.

All types decribes in this document reside in the namespace boost::utility.

Example

    #include <boost/static_assert.hpp>
    #define BOOST_SELECT_BY_SIZE_MAX_CASE 7  
    #include <boost/utility/select_by_size.hpp>

    using namespace boost::utility;

    case_<0> helper(bool);           
    case_<1> helper(int);            
    case_<2> helper(unsigned);
    case_<3> helper(long);
    case_<4> helper(unsigned long);
    case_<5> helper(float);
    case_<6> helper(double);
    case_<7> helper(const char*);

    struct test {
        static const int value =
            select_by_size< sizeof(helper("hello")) >::value;
        BOOST_STATIC_ASSERT(value == 7);
    };

    struct test2 {
        BOOST_SELECT_BY_SIZE(int, value, helper("hello"));
        BOOST_STATIC_ASSERT(value == 7);
    };

Description

The template case_

The defining property of the class template case_ is that the sizes of the types

    case_<-1>, case_<0>, case_<1>, case_<2>, case_<3>, ...
form a strictly increasing sequence. With well-behaved implementations, sizeof(case_<-1>) will be small, as will be the difference in size between adjacent types in the sequence.

The template select_by_size

The defining property of the class template select_by_size is that for each integer N in the range select_by_size through BOOST_SELECT_BY_SIZE_MAX_CASE, we have the equality

    select_by_size< sizeof(case_<N>) >::value == N

The macro BOOST_SELECT_BY_SIZE

The macro BOOST_SELECT_BY_SIZE(type, name, expr) can be used similarly to BOOST_STATIC_CONSTANT to define a static const intergral class member name of type type with value equal to select_by_size< sizeof(expr) >::value.

The macro BOOST_SELECT_BY_SIZE_MAX_CASE

In order to make use of select_by_size with specializations case_<N> for N other than -1, 0 and 1, the symbol BOOST_SELECT_BY_SIZE_MAX_CASE must be defined as an intergral value greater than or equal to N before including the header <boost/utility/select_by_size.hpp>. The header may be included multiple times with different values of BOOST_SELECT_BY_SIZE_MAX_CASE.

It is possible to implement select_by_size in such a way that this macro is not necessary. See Implementation, below, for a discussion.

Synopsis

    namespace boost {
      namespace utility {

        template<int N> struct case_ { [unspecified] };

        template<unsigned N> 
        struct select_by_size {
            struct type { 
                static const unsigned value = 
                    [M such that sizeof(case_<M>) == N];
            };
            static const unsigned value = type::value;
        };
 
        #define BOOST_SELECT_BY_SIZE(type, name, expr) [unspecified]
      } 
    }

Rationale

The utility of type_traits::yes_type and type_traits::no_type is well-known. The advantages of using select_by_size are:

Until recently the author knew of only one use for select_by_size with more than two cases, and so regarded it as a trick. He then discovered a second use, elevating it to a method. With the discovery of a third use, it is now a programming paradigm. (The three known uses are determing template arity, as in <boost/mpl/aux_/template_arity.hpp>, Joel de Guzman's type deduction system in the Boost Sandbox at <boost/utility/type_deduction.hpp>, and Reece Dunn's type deduction system for his Output Formatters library, also in the Boost Sandbox, at <boost/outfmt/detail/type_traits.hpp>. There are surely many more.)

Implementation

select_by_size is implemented by explicit specialization for the values

    sizeof(case_<-1>), sizeof(case_<0>), sizeof(case_<1>), sizeof(case_<2>), ... .
As a result, there is a limit to the number of cases which can be used by default.

There are several ways to remove this restriction. For instance:

This first approach is computationally too expensive for a widely used utility. The second approach is feasable; a sample implementation using alignment_of and max_align from the Type Traits library is given in the source code.

The present implementation was chosen to reduce dependencies on other libraries. If defining the macro BOOST_SELECT_BY_SIZE_MAX_CASE is considered too inconvenient, the default number of cases could be set to 10 or 20 with little noticeable overhead.

Dependencies

The header <boost/utility/select_by_size.hpp> depends on Boost.Config and the Boost Preprocessor Library.

Portability

The program <libs/utility/select_by_size_test.cpp> has been tested successfully with the following compilers:

It should work on any recent compiler for which Boost has been configured.

Feedback

The author, Jonathan Turkanis, can be contacted at turkanis@kangaroologic.com

© Copyright Jonathan Turkanis 2004. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.