Boost logo

Boost :

From: Borgerding, Mark A. (MarkAB_at_[hidden])
Date: 2000-02-10 18:27:36

Hash: SHA1

> -----Original Message-----
> From: Darin Adler [mailto:darin_at_[hidden]]
> Sent: Thursday, February 10, 2000 2:51 PM
> To: Boost
> Subject: [boost] Re: Compile time assertions
> Mark Borgerding:
> > Any shortcomings besides the misuse example in bar() above?
> It's a nice implementation, but I'm not sure it's better than
> the ones I
> presented. Here are a few shortcomings I noticed right away.

Thanks for the feedback. I appreciate the time you spent looking
things over.
It looks like my approach may have a problem or two with respect to
the requirements set forth. However, I still think it is more
portable and flexible than any of the previous solutions. The only
indisputable shortcoming is that it will pad the size of a structure
that contains it, but there are ways to work around that.

I really like the approach presented by Steve Cleary. It's a clever
idea to use contradictory typedefs to flag errors. However, it
sounds like the compiler support for this is still rather spotty.

I believe that the requirements are optimistic at best, at worst they
are completely unrealistic. The criteria sometimes contradict each
other. For example; the solution must be callable from anywhere in
the same way as assert, but without the aid of macros. I don't know
of any way
        class foo{
          POSTULATE(sizeof(char) == 1);
can compile without using a macro.

Unless there can be some loosening of the requirements, it would seem
there can never be a compile-time assertion class for boost. Pity.
It is a worthwhile tool to have in one's BOT.

There are several reasons I prefer the template<bool> method to other

  1. no macros, although use becomes slightly easier if they're
  2. Standard. Portable to any compiler that has explicit template
  3. Simple concept. Construct a ctassert<true>, nothing happens.
Construct a ctassert<false>, compile error.
  4. Better supported than non-standard language features like
unnamed zero-length bit fields.
          The method E for which you expressed a preferrence may be elegant,
but it fails on MSVC6 and g++ 2.95.2, two very popular compilers
        MSVC error:
           error C2625: anonymous union did not declare any data members
        g++ error:
           anonymous class type not used to declare any objects

> If used in a class definition, it doesn't give an error
> message on the line
> where the assertion fails. It doesn't fail at all unless an
> object of the
> class is instantiated (fails requirements R1, 5, 8, 9).

Well that depends upon your definition of where the assertion fails.
As I see it, the assertion is tested at the code that instantiates
the ctassert<bool> object. This is behavior is predictable.

The approach is similar in usage to a call to assert(), but it is
more flexible in that it can appear at the file or namespace scope,
whereas a call to assert cannot. It can also appear inside class
definitions, but as you point out below there are problems with that.

> It can affect structure padding if used between elements in a
> structure as
> discussed on Jay's web page (fails requirement 2).

Ah good point. It would probably best then to avoid using them
inside structures and classes, since all structures must have a
non-zero size. Inheriting from cassert seems to be fairly
bloat-safe. But I think that inheriting from such a class is a
pretty ugly thing to impose on a class (remember the "is a" rule).

I question that the restriction about appearing anywhere (class
definition/implementation) is really a necessary one. It contradicts
the analogy to the assert() function. The closer the use of
postulate/ctassert is to that of assert(), the more readable the code
will likely be for someone who is not familiar with the concept.

I think any compile-time check that can exist in the class definition
can also exist in a member function or in a namespace.


class foo{
... other stuff
        POSTULATE(sizeof(char) == 1);
        POSTULATE(sizeof(short) == 2);

can be moved into
class foo{
... other stuff
   int check() {
        POSTULATE(sizeof(char) == 1);
        POSTULATE(sizeof(short) == 2);

This code would get compiled, but not linked into the executable
since it is never called.

For templates, the case is a little more complex. Compilation of an
uncalled function cannot be guaranteed. But by moving the postulate
code into the constructors or a destructor, compilation can be
assured without adding any extra weight.

> The error message from my compiler is "illegal access to
> protected/private
> member" (fails requirements 7, 9).

Hmm, I thought it would be a safe assumption that all compilers would
at least name the class to be instantiated. g++ and MSVC give
reasonable messages. What compiler are you using? The name
ctassert, compile_time_check or something like that along with the
source file & line number should be enough to give a person an idea
of what's wrong.

The errors below are quotes results from g++ and MSVC upon compiling
the attached code.
line 6 is

g++ error:
test.cpp: In function `int main()':
test.cpp:6: `ctassert<false>::ctassert()' is private
test.cpp:12: within this context

MSVC error:
test.cpp(12) : error C2248: 'ctassert<0>::ctassert<0>' : cannot
access private member declared in class 'ctassert<0>'
                test.cpp(6) : see declaration of 'ctassert<0>::ctassert<0>'

I doubt any solution would reliably tell the user something much more
descriptive, like
        yourfile.cpp(45) error: compile time check "sizeof(int) == 4" has
on every platform. Although this would be great if it were possible.
 If macros are allowed, then the stringizing operator # might be used
to give the specific expression that failed.

- -- Mark

Version: PGPfreeware 6.5.2 for non-commercial use <>


Boost list run by bdawes at, gregod at, cpdaniel at, john at