|
Boost : |
From: Borgerding, Mark A. (MarkAB_at_[hidden])
Date: 2000-02-10 18:27:36
-----BEGIN PGP SIGNED MESSAGE-----
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
methods.
1. no macros, although use becomes slightly easier if they're
allowed.
2. Standard. Portable to any compiler that has explicit template
specialization.
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.
e.g.
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
ctassert<compiledValue>();
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
failed
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
-----BEGIN PGP SIGNATURE-----
Version: PGPfreeware 6.5.2 for non-commercial use <http://www.pgp.com>
iQA/AwUBOKNJbf9Ej8AEXMygEQIUZACg6Vm6kqHfebQcC6qktzrlce7gLeMAoLbN
M2xjRvBwgM8NDBhV5pJfqcvq
=cxnW
-----END PGP SIGNATURE-----
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk