From 7cf64600016fbf81b0fb32baed28452e1ec658a4 Mon Sep 17 00:00:00 2001 From: Joel de Guzman Date: Fri, 10 Oct 2003 21:15:15 +0000 Subject: [PATCH] type deduction utility [SVN r1662] --- include/boost/utility/type_deduction.hpp | 476 +++++++++++++++++++++++ type_deduction_tests.cpp | 343 ++++++++++++++++ 2 files changed, 819 insertions(+) create mode 100644 include/boost/utility/type_deduction.hpp create mode 100644 type_deduction_tests.cpp diff --git a/include/boost/utility/type_deduction.hpp b/include/boost/utility/type_deduction.hpp new file mode 100644 index 0000000..ba07f1f --- /dev/null +++ b/include/boost/utility/type_deduction.hpp @@ -0,0 +1,476 @@ +/*============================================================================= + Copyright (c) 2001-2003 Joel de Guzman + + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#ifndef BOOST_TYPE_DEDUCTION_IPP +#define BOOST_TYPE_DEDUCTION_IPP + +/*============================================================================= + + Return Type Deduction + [JDG Sept. 15, 2003] + + Before C++ adopts the typeof, there is currently no way to deduce the + result type of an expression such as x + y. This deficiency is a major + problem with template metaprogramming; for example, when writing + forwarding functions that attempt to capture the essence of an + expression inside a function. Consider the std::plus: + + template + struct plus : public binary_function + { + T operator()(T const& x, T const& y) const + { + return x + y; + } + }; + + What's wrong with this? Well, this functor does not accurately capture + the behavior of the plus operator. 1) It does not handle the case where + x and y are of different types (e.g. x is short and y is int). 2) It + assumes that the arguments and return type are the same (i.e. when + adding a short and an int, the return type ought to be an int). Due to + these shortcomings, std::plus(x, y) is a poor substitute for x + y. + + The case where x is short and y is int does not really expose the + problem. We can simply use std::plus and be happy that the + operands x and y will simply be converted to an int. The problem + becomes evident when an operand is a user defined type such as bigint. + Here, the conversion to bigint is simply not acceptable. Even if the + unnecessary conversion is tolerable, in generic code, it is not always + possible to choose the right T type that can accomodate both x and y + operands. + + To truly model the plus operator, what we need is a polymorphic functor + that can take arbitrary x and y operands. Here's a rough schematic: + + struct plus + { + template + unspecified-type + operator()(X const& x, Y const& y) const + { + return x + y; + } + }; + + Now, we can handle the case where X and Y are arbitrary types. We've + solved the first problem. To solve the second problem, we need some + form of return type deduction mechanism. If we had the typeof, it would + be something like: + + template + typeof(X() + Y()) + operator()(X const& x, Y const& y) const + { + return x + y; + } + + Without the typeof facility, it is only possible to wrap an expression + such as x + y in a function or functor if we are given a hint that + tells us what the actual result type of such an expression is. Such a + hint can be in the form of a metaprogram, that, given the types of the + arguments, will return the result type. Example: + + template + struct result_of_plus + { + typedef unspecified-type type; + }; + + Given a result_of_plus metaprogram, we can complete our polymorphic + plus functor: + + struct plus + { + template + typename result_of_plus::type + operator()(X const& x, Y const& y) const + { + return x + y; + } + }; + + The process is not automatic. We have to specialize the metaprogram for + specific argument types. Examples: + + template <> + struct result_of_plus + { + typedef int type; + }; + + template + struct result_of_plus, std::complex > + { + typedef std::complex type; + }; + + To make it easier for the user, specializations are provided for common + types such as primitive c++ types (e.g. int, char, double, etc.), and + standard types (e.g. std::complex, iostream, std containers and + iterators). + + To further improve the ease of use, for user defined classes, we can + supply a few more basic specializations through metaprogramming using + heuristics based on canonical operator rules (Such heuristics can be + found in the LL and Phoenix, for example). For example, it is rather + common that the result of x += y is X& or the result of x || y is a + bool. The client is out of luck if her classes do not follow the + canonical rules. She'll then have to supply her own specialization. + + The type deduction mechanism demostrated below approaches the problem + not through specialization and heuristics, but through a limited form + of typeof mechanism. The code does not use heuristics, hence, no + guessing games. The code takes advantage of the fact that, in general, + the result type of an expression is related to one its arguments' type. + For example, x + y, where x has type int and y has type double, has the + result type double (the second operand type). Another example, x[y] + where x is a vector and y is a std::size_t, has the result type + vector::reference (the vector's reference type type). + + The limited form of type deduction presented can detect common + relations if the result of a binary or unary operation, given arguments + x and y with types X and Y (respectively), is X, Y, X&, Y&, X*, Y*, X + const*, Y const*, bool, int, unsigned, double, container and iterator + elements (e.g the T, where X is: T[N], T*, vector, map, + vector::iterator). More arguments/return type relationships can be + established if needed. + + A set of overloaded test(T) functions capture these argument related + types. Each test(T) function returns a distinct type that can be used + to determine the exact type of an expression. + + Consider: + + template + x_value_type + test(X const&); + + template + y_value_type + test(Y const&); + + Given an expression x + y, where x is int and y is double, the call to: + + test(x + y) + + will return a y_value_type. + + Now, if we rig x_value_type and y_value_type such that both have unique + sizes, we can use sizeof(test(x + y)) to determine if the result + type is either X or Y. + + For example, if: + + sizeof(test(x + y)) == sizeof(y_value_type) + + then, we know for sure that the result of x + y has type Y. + + The same basic scheme can be used to detect more argument-dependent + return types where the sizeof the test(T) return type is used to index + through a boost::mpl vector which holds each of the corresponding + result types. + +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { + +struct error_cant_deduce_type {}; + + namespace type_deduction_detail + { + typedef char(&bool_value_type)[1]; + typedef char(&int_value_type)[2]; + typedef char(&uint_value_type)[3]; + typedef char(&double_value_type)[4]; + + typedef char(&bool_reference_type)[5]; + typedef char(&int_reference_type)[6]; + typedef char(&uint_reference_type)[7]; + typedef char(&double_reference_type)[8]; + + typedef char(&x_value_type)[9]; + typedef char(&x_reference_type)[10]; + typedef char(&x_const_pointer_type)[11]; + typedef char(&x_pointer_type)[12]; + + typedef char(&y_value_type)[13]; + typedef char(&y_reference_type)[14]; + typedef char(&y_const_pointer_type)[15]; + typedef char(&y_pointer_type)[16]; + + typedef char(&container_reference_type)[17]; + typedef char(&container_const_reference_type)[18]; + typedef char(&container_mapped_type)[19]; + + typedef char(&cant_deduce_type)[20]; + + template ::type> + struct is_basic + : mpl::or_< + is_same + , is_same + , is_same + , is_same + > {}; + + template + struct reference_type + { + typedef typename C::reference type; + }; + + template + struct reference_type + { + typedef T& type; + }; + + template + struct reference_type + { + typedef T& type; + }; + + template + struct const_reference_type + { + typedef typename C::const_reference type; + }; + + template + struct mapped_type + { + typedef typename C::mapped_type type; + }; + + struct asymmetric; + + template + cant_deduce_type + test(...); // The black hole !!! + + template + bool_value_type + test(bool const&); + + template + int_value_type + test(int const&); + + template + uint_value_type + test(unsigned const&); + + template + double_value_type + test(double const&); + + template + bool_reference_type + test(bool&); + + template + int_reference_type + test(int&); + + template + uint_reference_type + test(unsigned&); + + template + double_reference_type + test(double&); + + template + typename disable_if< + mpl::or_, is_const > + , x_value_type + >::type + test(X const&); + + template + typename disable_if< + is_basic + , x_reference_type + >::type + test(X&); + + template + typename disable_if< + mpl::or_< + is_basic + , is_const + > + , x_const_pointer_type + >::type + test(X const*); + + template + x_pointer_type + test(X*); + + template + typename disable_if< + mpl::or_< + is_basic + , is_same + , is_const + , is_same + > + , y_value_type + >::type + test(Y const&); + + template + typename disable_if< + mpl::or_< + is_basic + , is_same + , is_same + > + , y_reference_type + >::type + test(Y&); + + template + typename disable_if< + mpl::or_< + is_same + , is_const + , is_same + > + , y_const_pointer_type + >::type + test(Y const*); + + template + typename disable_if< + mpl::or_< + is_same + , is_same + > + , y_pointer_type + >::type + test(Y*); + + template + typename disable_if< + is_basic + , container_reference_type + >::type + test(typename X::reference); + + template + typename enable_if< + mpl::and_< + mpl::or_, is_pointer > + , mpl::not_ > + > + , container_reference_type + >::type + test(Z&); + + template + typename disable_if< + is_basic + , container_const_reference_type + >::type + test(typename X::const_reference); + + template + typename disable_if< + is_basic + , container_mapped_type + >::type + test(typename X::mapped_type); + + template + struct base_result_of + { + typedef typename remove_reference::type x_type; + typedef typename remove_reference::type y_type; + + typedef mpl::vector20< + mpl::identity + , mpl::identity + , mpl::identity + , mpl::identity + , mpl::identity + , mpl::identity + , mpl::identity + , mpl::identity + , mpl::identity + , mpl::identity + , mpl::identity + , mpl::identity + , mpl::identity + , mpl::identity + , mpl::identity + , mpl::identity + , reference_type + , const_reference_type + , mapped_type + , mpl::identity + > + types; + }; + +}} // namespace boost::type_deduction_detail + +#define BOOST_RESULT_OF_COMMON(expr, name, Y, SYMMETRY) \ + struct name \ + { \ + typedef type_deduction_detail::base_result_of base_type; \ + static typename base_type::x_type x; \ + static typename base_type::y_type y; \ + \ + BOOST_STATIC_CONSTANT(int, \ + size = sizeof( \ + type_deduction_detail::test< \ + typename base_type::x_type \ + , SYMMETRY \ + >(expr) \ + )); \ + \ + BOOST_STATIC_CONSTANT(int, index = (size / sizeof(char)) - 1); \ + \ + typedef typename mpl::at_c< \ + typename base_type::types, index>::type id; \ + typedef typename id::type type; \ + }; + +#define BOOST_UNARY_RESULT_OF(expr, name) \ + template \ + BOOST_RESULT_OF_COMMON(expr, name, \ + type_deduction_detail::asymmetric, type_deduction_detail::asymmetric) + +#define BOOST_BINARY_RESULT_OF(expr, name) \ + template \ + BOOST_RESULT_OF_COMMON(expr, name, Y, typename base_type::y_type) + +#define BOOST_ASYMMETRIC_BINARY_RESULT_OF(expr, name) \ + template \ + BOOST_RESULT_OF_COMMON(expr, name, Y, type_deduction_detail::asymmetric) + +#endif diff --git a/type_deduction_tests.cpp b/type_deduction_tests.cpp new file mode 100644 index 0000000..6bf4079 --- /dev/null +++ b/type_deduction_tests.cpp @@ -0,0 +1,343 @@ +/*============================================================================= + Copyright (c) 2001-2003 Joel de Guzman + + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost +{ + BOOST_UNARY_RESULT_OF(-x, result_of_negate); + BOOST_UNARY_RESULT_OF(+x, result_of_posit); + BOOST_UNARY_RESULT_OF(!x, result_of_logical_not); + BOOST_UNARY_RESULT_OF(~x, result_of_invert); + BOOST_UNARY_RESULT_OF(&x, result_of_reference); + BOOST_UNARY_RESULT_OF(*x, result_of_dereference); + + BOOST_UNARY_RESULT_OF(++x, result_of_pre_increment); + BOOST_UNARY_RESULT_OF(--x, result_of_pre_decrement); + BOOST_UNARY_RESULT_OF(x++, result_of_post_increment); + BOOST_UNARY_RESULT_OF(x--, result_of_post_decrement); + + BOOST_BINARY_RESULT_OF(x = y, result_of_assign); + BOOST_ASYMMETRIC_BINARY_RESULT_OF(x[y], result_of_index); + + BOOST_BINARY_RESULT_OF(x += y, result_of_plus_assign); + BOOST_BINARY_RESULT_OF(x -= y, result_of_minus_assign); + BOOST_BINARY_RESULT_OF(x *= y, result_of_multiplies_assign); + BOOST_BINARY_RESULT_OF(x /= y, result_of_divides_assign); + BOOST_BINARY_RESULT_OF(x %= y, result_of_modulus_assign); + + BOOST_BINARY_RESULT_OF(x &= y, result_of_and_assign); + BOOST_BINARY_RESULT_OF(x |= y, result_of_or_assign); + BOOST_BINARY_RESULT_OF(x ^= y, result_of_xor_assign); + BOOST_BINARY_RESULT_OF(x <<= y, result_of_shift_left_assign); + BOOST_BINARY_RESULT_OF(x >>= y, result_of_shift_right_assign); + + BOOST_BINARY_RESULT_OF(x + y, result_of_plus); + BOOST_BINARY_RESULT_OF(x - y, result_of_minus); + BOOST_BINARY_RESULT_OF(x * y, result_of_multiplies); + BOOST_BINARY_RESULT_OF(x / y, result_of_divides); + BOOST_BINARY_RESULT_OF(x % y, result_of_modulus); + + BOOST_BINARY_RESULT_OF(x & y, result_of_and); + BOOST_BINARY_RESULT_OF(x | y, result_of_or); + BOOST_BINARY_RESULT_OF(x ^ y, result_of_xor); + BOOST_BINARY_RESULT_OF(x << y, result_of_shift_left); + BOOST_BINARY_RESULT_OF(x >> y, result_of_shift_right); + + BOOST_BINARY_RESULT_OF(x == y, result_of_equal_to); + BOOST_BINARY_RESULT_OF(x != y, result_of_not_equal_to); + BOOST_BINARY_RESULT_OF(x < y, result_of_less); + BOOST_BINARY_RESULT_OF(x <= y, result_of_less_equal); + BOOST_BINARY_RESULT_OF(x > y, result_of_greater); + BOOST_BINARY_RESULT_OF(x >= y, result_of_greater_equal); + + BOOST_BINARY_RESULT_OF(x && y, result_of_logical_and); + BOOST_BINARY_RESULT_OF(x || y, result_of_logical_or); + BOOST_BINARY_RESULT_OF(true ? x : y, result_of_if_else); +} + +using namespace boost; +using namespace std; + +struct X {}; +X operator+(X, int); + +struct Y {}; +Y* operator+(Y, int); + +struct Z {}; +Z const* operator+(Z const&, int); +Z& operator+(Z&, int); +bool operator==(Z, Z); +bool operator==(Z, int); + +struct W {}; +Z operator+(W, int); +bool operator==(W, Z); + +int +test_main(int, char*[]) +{ + // PLUS + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus, double>::type result; + BOOST_STATIC_ASSERT((is_same >::value)); + } + { + typedef result_of_plus >::type result; + BOOST_STATIC_ASSERT((is_same >::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // INDEX + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index, int>::type result; + BOOST_STATIC_ASSERT((is_same::reference>::value)); + } + { + typedef result_of_index const, int>::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index const, int>::type result; + BOOST_STATIC_ASSERT((is_same::const_reference>::value)); + } + { + typedef result_of_index, int>::type result; + BOOST_STATIC_ASSERT((is_same::reference>::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::iterator, int>::type result; + BOOST_STATIC_ASSERT((is_same::iterator::reference>::value)); + } + { + typedef result_of_index::const_iterator, int>::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::const_iterator, int>::type result; + BOOST_STATIC_ASSERT((is_same::const_iterator::reference>::value)); + } + { + typedef result_of_index, char>::type result; + BOOST_STATIC_ASSERT((is_same::mapped_type>::value)); + } + + // PLUS ASSIGN + { + typedef result_of_plus_assign::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus_assign::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus_assign, double>::type result; + BOOST_STATIC_ASSERT((is_same&>::value)); + } + + // SHIFT LEFT + { + typedef result_of_shift_left::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_shift_left::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_shift_left::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // EQUAL + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // MINUS (pointers) + { + typedef result_of_minus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // DEREFERENCE + { + typedef result_of_dereference::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_dereference::iterator>::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_dereference >::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // ADDRESS OF + { + typedef result_of_reference::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_reference::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // PRE INCREMENT + { + typedef result_of_pre_increment::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // POST INCREMENT + { + typedef result_of_post_increment::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // IF-ELSE-EXPRESSION ( c ? a : b ) + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // DEDUCTION FAILURE + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + return 0; +}