Boost logo

Boost :

From: John Maddock (john_maddock_at_[hidden])
Date: 2002-03-24 07:15:37


Wow!, I'm very impressed, or at least I think I am - the interface for
message formatting is a little "difficult" on first reading, but then maybe
it has to be - if only string literals could be used as template parameters
then it could probably be simplified I guess. Then again on a second pass
though maybe it doesn't look so bad...

I would guess that Howard is going to be salivating over the "test compile
any code" option :-)

John Maddock
http://ourworld.compuserve.com/homepages/john_maddock/index.htm
>
> Hello,
> STL and Boost error messages are horrific. Concept checking comes up
with
> slightly better error messages, but these messages still have several
> disadvantages:
> - Compilers print out huge instantiation stacks
> - The formatting of messages is subject to the formatting of type names
> - The_Messages_Tend_To_Be_Hard_To_Read
>
> This is a problem with every library, but it is much worse in highly
> templated libraries like many of those in Boost. These bad error messages
are
> a huge barrier to entry for newcomers, but there doesn't seem to be much
we
> can do within the language to make it better... so why not extend?
>
> I've implemented a tiny extension to GCC that allows template
metaprograms
> two new abilities:
> 1) Format and print messages, warnings, and errors via the compiler's
> internal diagnostic routines from a template metaprogram
> 2) Determine if the instantiation of a given class will cause any errors
at
> compile-time, but do NOT emit any error messages (this is a
> compiler-supported can_instantiate type trait).
>
> Before describing the interface, here's an example based on
Boost.Function.
> Take this bogus code:
>
> class X {};
> int foo(X*);
> boost::function<int, float> f(&foo);
>
> The error message looks something like this:
>
> -----------------------------------------------------------------
> ../../../boost/function/function_template.hpp: In static member function
> `static R boost::detail::function::function_invoker1<FunctionPtr, R,
> T0>::invoke(boost::detail::function::any_pointer, T0) [with FunctionPtr
=
> int (*)(X*), R = int, T0 = float]':
> ../../../boost/function/function_template.hpp:461: instantiated from
`void
> boost::function1<R, T0, Policy, Mixin, Allocator>::assign_to(FunctionPtr,
> boost::detail::function::function_ptr_tag) [with FunctionPtr = int
(*)(X*), R
> = int, T0 = float, Policy = boost::empty_function_policy, Mixin =
> boost::empty_function_mixin, Allocator =
> std::allocator<boost::function_base>]'
> ../../../boost/function/function_template.hpp:444: instantiated from
`void
> boost::function1<R, T0, Policy, Mixin, Allocator>::assign_to(Functor)
[with
> Functor = int (*)(X*), R = int, T0 = float, Policy =
> boost::empty_function_policy, Mixin = boost::empty_function_mixin,
Allocator
> = std::allocator<boost::function_base>]'
> ../../../boost/function/function_template.hpp:278: instantiated from
> `boost::function1<R, T0, Policy, Mixin, Allocator>::function1(Functor)
[with
> Functor = int (*)(X*), R = int, T0 = float, Policy =
> boost::empty_function_policy, Mixin = boost::empty_function_mixin,
Allocator
> = std::allocator<boost::function_base>]'
> ../../../boost/function.hpp:475: instantiated from `boost::function<R,
T1,
> T2, T3, T4, T5, T6, T7, T8, T9, T10>::function(Functor) [with Functor =
int
> (*)(X*), R = int, T1 = float, T2 = boost::detail::function::unusable, T3 =
> boost::detail::function::unusable, T4 = boost::detail::function::unusable,
T5
> = boost::detail::function::unusable, T6 =
boost::detail::function::unusable,
> T7 = boost::detail::function::unusable, T8 =
> boost::detail::function::unusable, T9 = boost::detail::function::unusable,
> T10 = boost::detail::function::unusable]'
> bad_error_eg.cpp:29: instantiated from here
> ../../../boost/function/function_template.hpp:81: cannot convert `float'
to
> `X*
> ' in argument passing
> -----------------------------------------------------------------
>
> With an updated Boost.Function and this extension to GCC, the error
message
> becomes:
>
> bad_error_eg.cpp:29: objects of type 'int (*)(X*)' cannot be invoked with
> Boost.Function argument types '(float)'
>
> Note a few important things: there is no instantiation stack, and there
are
> no additional errors before or after this two-line error message. Only
> information relevant to the misuse of Boost.Function is given, and all of
the
> information given is determine by the _library author_, not the compiler.
>
> If you're interested in the modifications to Boost.Function to achieve
this,
> please check out the `compiler_supported_error_messages' branch in CVS.
The
> affected files are (in order of importance):
> boost/function/errors.hpp
> boost/function/function_template.hpp
> boost/function.hpp
>
> (The newer error messages are enabled with the BOOST_COMPILER_HOOKS macro)
>
> If you're interested in the interface with the compiler, read on...
>
> -----------Emitting diagnostics------------
> Diagnostics are emitted using several class templates located in the
header
> <ext/compiler>. These class templates are named 'print', 'warning', and
> 'error' to print simple messages, warnings, and errors, respectively. Each
is
> instantiated with 3 template arguments:
> - a 'message' type
> - a list of arguments (default = no arguments)
> - an instantiation depth to suppress (default = 0)
>
> A message type is just a type with a static constant character string
named
> 'text', whose definition must be visible. For instance, the 'Hello,
World!'
> message would look like this:
>
> struct HelloWorld {
> static const char* text;
> };
> const char* HelloWorld::text = "Hello, World!";
>
> Messages may contain three types of printf-style placeholders:
> - %t denotes a type
> - %v denotes a (compile-time) value (5, &foo, true, etc.)
> - %s denotes a message string (e.g., another message class)
>
> The list of arguments is a typelist containing the arguments that will
> replace the printf-sytle placeholders. The 'arg' class is a typelist
> constructor (i.e., 'cons' in LISP) and each element in the list is either:
> - type<T> (where 'T' is the type to print for the corresponding %t)
> - value<T, value> (where 'T' is the type of 'value', and 'value' is a
> compile-time value; this corresponds to a '%v')
> - string<T> (where 'T' is a message type to print for the corresponding
> '%s')
>
> Here's a quick example:
> struct the_type_is {
> static const char* text;
> };
> const char* the_type_is::text = "The type is: %t";
>
> sizeof(error<the_type_is, arg<type<int> > > ); // #1
>
> This will print an error message at the line marked #1 that reads 'The
type
> is: int'
>
> --------------More advanced formatting--------------
> More advanced error messages might not be easily encoded in a single
string.
> The same formatting constructs that are used in diagnostics can be used to
> format new strings, sprintf-style. The class template 'format' has the
> following interface:
>
> template<typename Msg, typename Args = /* no arguments*/ >
> struct format {
> static const char* text;
> };
>
> Again, 'Msg' is a message (with a 'text' field) and Args is the set of
> arguments that will be substituted for the %t, %v, and %s placeholders in
the
> message. This instance of format is itself a message class: the compiler
> formats the message and places the result into the default initializer of
> 'text' so that a message can be printed. This could allow, for instance,
much
> more sophisticated concept checking, such as:
>
> <some file>:<some_line> Type 'std::my_ra_iterator' does not model the
> RandomAccessIterator concept because the following expressions are not
> supported (for std::my_ra_iterator x and int n):
> x += n
> x -= n
> n + x
>
> ---------------can_instantiate trait-----------------
> The can_instantiate trait is relatively simple. It's interface is:
>
> template<class _ToInstantiate>
> struct can_instantiate {
> // The compiler will change this value to 'true' if the template
> // parameter can safely be instantiated
> enum { value = false };
> };
>
> If the type '_ToInstantiate' can be instantiated, then
> can_instantiate<_ToInstantiate>::value will be true, and false otherwise.
And
> because the instantiation of a class can trigger the instantiation of
> arbitrary code segments, it allows the metaprogram to 'test compile' code
> without breaking the rest of the compilation.
>
> ----------------------------------------------------
>
> For reference, the source to the 'ext/compiler' header is at the end of
the
> message. The pragmas aren't part of the interface, but serve as a link
with
> the GCC extension. I've extended mainline GCC (3.2 prerelease) to use
these
> pragmas, and the code here and on the Boost CVS branch has been tested
under
> this modified compiler. If there is interest, I can make patches
available.
>
> I believe this is on-topic for Boost (because it is a compiler-supported
> library that aids in library construction), but I apologize if it is
> off-topic.
>
> Comments would be appreciated.
>
> Doug
>
> #ifndef __GLIBCPP_INTERNAL_COMPILER_H
> #define __GLIBCPP_INTERNAL_COMPILER_H
>
> #pragma GCC system_header
>
> namespace __gnu_cxx
> {
>
> template<class _Arg, class _Next = void>
> struct arg {
> typedef _Arg arg_type;
> typedef _Next next;
> };
>
> template<class _Msg> class string {};
>
> template<class _Type> class type {};
>
> template<class _Type, _Type _Value> class value {};
>
>
> #pragma GCC compile_time_action message
> template<class _Msg, class _Next = void, int SuppressDepth = 0>
> class print {};
>
> #pragma GCC compile_time_action warning
> template<class _Msg, class _Next = void, int SuppressDepth = 0>
> class warning {};
>
> #pragma GCC compile_time_action error
> template<class _Msg, class _Next = void, int SuppressDepth = 0>
> class error {};
>
> #pragma GCC compile_time_action format
> template<class _Msg, class _Next = void>
> struct format {
> static const char* text;
> };
>
> #pragma GCC compile_time_action try_compile
> template<class _ToInstantiate>
> struct can_instantiate {
> // The compiler will change this value to 'true' if the template
> // parameter can safely be instantiated
> enum { value = false };
> };
>
> } // namespace __gnu_gcc
>
> #endif /* __GLIBCPP_INTERNAL_COMPILER_H */
>
> // Local Variables:
> // mode:C++
> // End:
> _______________________________________________
> Unsubscribe & other changes:
http://lists.boost.org/mailman/listinfo.cgi/boost
>


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