Boost logo

Boost :

From: Eric Niebler (eric_at_[hidden])
Date: 2008-04-13 13:30:22


Eric Niebler wrote:
> I recently had a C++ problem and found an answer that tickled my brain.
> In the spirit of Car Talk on NPR, I thought I'd share it in the form of
> a puzzler.

OK, here's my solution. It's no better than some of the others I've seen
posted here. Just a different way to skin the cat.

#include <boost/mpl/assert.hpp>
#include <boost/preprocessor.hpp>
#include <boost/type_traits/is_same.hpp>

#define M 10

struct Default {};
struct ignore { ignore(...); };

template<typename E>
struct nondeducable
{
     typedef nondeducable type;
     nondeducable(E*&);
     nondeducable(Default*&);
};

template<>
struct nondeducable<Default>
{
     typedef nondeducable type;
     nondeducable(Default*&);
};

template<int N, BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(M, class A, void)>
struct select_nth
{
     BOOST_MPL_ASSERT_MSG((false), NO_COMMON_TYPE, (select_nth));
};

#define M0(Z, N, _) \
template<BOOST_PP_ENUM_PARAMS_Z(Z, M, class A)> \
struct select_nth<N BOOST_PP_ENUM_TRAILING_PARAMS_Z(Z, M, A)> \
{ \
     typedef BOOST_PP_CAT(A, N) type; \
};
BOOST_PP_REPEAT(M, M0, ~)
#undef M0

template<BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(M, class A, Default)>
struct common_type
{
     static char (&deducer(BOOST_PP_ENUM_PARAMS(M, ignore
         BOOST_PP_INTERCEPT)))[M+1];

     #define M0(Z, N, _) \
     static BOOST_PP_CAT(A, N) *BOOST_PP_CAT(a, N); \
     template<typename T> \
     static char (&deducer( \
         BOOST_PP_ENUM_PARAMS_Z(Z, N, Default *& BOOST_PP_INTERCEPT) \
         BOOST_PP_COMMA_IF(N) T *& \
         BOOST_PP_ENUM_TRAILING_PARAMS_Z( \
             Z \
           , BOOST_PP_DEC(BOOST_PP_SUB(M, N)) \
           , typename nondeducable<T>::type BOOST_PP_INTERCEPT \
         ) \
     ))[N+1];
     BOOST_PP_REPEAT(M, M0, ~)
     #undef M0
     static int const value = sizeof(deducer(
         BOOST_PP_ENUM_PARAMS(M, a))) - 1;
     typedef typename select_nth<value
         BOOST_PP_ENUM_TRAILING_PARAMS(M, A)>::type type;
};

int main()
{
     using boost::is_same;
     BOOST_MPL_ASSERT((is_same<common_type<Default, Default, int,
         Default>::type, int>));
     BOOST_MPL_ASSERT((is_same<common_type<int, Default, int,
         Default>::type, int>));
     BOOST_MPL_ASSERT((is_same<common_type<int, int, int, int>::type,
         int>));
     BOOST_MPL_ASSERT((is_same<common_type<Default, Default,
         Default>::type, Default>));
     // MPL assertion failure
     //common_type<int, short, Default>::type t;
}

The gist is to build an overload set like:

deducer(T, Non<T>::type, Non<T>::type)
deducer(Default, T, Non<T>::type, Non<T>::type)
deducer(Default, Default, T, Non<T>::type)
deducer(Default, Default, Default, T)

Where both T and Default are convertible to Non<T>::type. The overload
that gets selected has deduced T to be the common type. With decltype we
could read it out directly. Instead I encode the index of T in the
return type. There is one additional overload that gets selected only if
there is no common type.

If a common type exists, we instantiate nondeducable<T>, possibly
nondeducable<Default>, and a select_nth template. That's it.

-- 
Eric Niebler
Boost Consulting
www.boost-consulting.com

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