Tuesday, July 14, 2009

User-friendly compile errors for templates in C++0x

The C++0x features decltype, static_assert and the "new function declarator syntax" can be combined with our old friend SFINAE to generate nicer template compile errors.

As a simple example, consider a container class similar to std::set. Normally if you just declare a variable

set<my_type> s;

it will compile without error even if my_type has no operator<. You will only get an error when you try to call a set member function, such as insert(). Worse still, the errors tend to be quite verbose. (Too verbose for me to want to paste here, anyway.) It would be really nice to generate a short, readable error at the point of the original variable declaration. Let's see how we can do just that in C++0x.

First, we need to write a compile-time test for operator<. This is where SFINAE, decltype and the new function declarator syntax come together. We write the test function:

auto less_than_test(const T* t)
-> decltype(*t < *t, char(0));

and the fallback overload:

std::array<char, 2> less_than_test(...);

The trick here is that, according to the C++0x grammar, we have:

decltype ( expression )

and

expression:
assignment-expression
expression , assignment-expression

This means that the first overload uses decltype to do two things: it makes the overload a viable candidate only if the expression *t < *t is valid; and it says the overload returns a char.

Second, we can use sizeof to determine which of the overloads is selected for a given type T, and static_assert to generate a readable error:

template <typename T>
class set
{
public:
static_assert(
sizeof(less_than_test((T*)0)) == 1,
"type T must provide operator<");
};

The g++ 4.4 compiler then gives the following output on the original variable declaration:

test.cpp: In instantiation of set<my_type>
test.cpp:21: instantiated from here
test.cpp:13: error: static assertion failed:
"type T must provide operator<"

It works with function templates too. To add a check to Asio's async_read function's ReadHandler parameter, I could write the check as follows:

template <typename T>
auto read_handler_test(T* t)
-> decltype(
(*t)(
*(const error_code*)0,
(const std::size_t)0),
char(0));

std::array<char, 2> read_handler_test(...);

template <..., typename ReadHandler>
void async_read(..., ReadHandler handler)
{
static_assert(
sizeof(read_handler_test(&handler)) == 1,
"ReadHandler type requirements not met");
...
}

Perhaps with a touch of macro magic, checks of this sort could become quite easy to write.

"Hang on, what about C++0x concepts?" I hear you ask. What are they? ;-)

6 comments:

john said...

آهنگ جدید
سینا سرلک بدون تو
دانلود آهنگ بهنام بانی زخم کاری
آهنگ مسعود صادقلو زندگی خودمه

Unknown said...

바카라사이트 Thanks for sharing the informative post. If you are looking the Linksys extender setup guidelines . so, we have a best technical expert for handlings your quires. for more information gets touch with us

Unknown said...

카지노사이트 Great writing to see, glad that google brought me here, Keep Up cool job

Unknown said...

스포츠토토 I constantly spent my half an hour to read this web site’s content daily along with a cup
of coffee.

Unknown said...

토토사이트 I was able to find good information from your content.

Kaylee Brown said...

Thanks for sharing this outstanding post with such novice people like us. This article is helpful for both those who offer and those who seeking programming help online.