[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Chapter summary: When you want a
std::string
, usevcl_string
.
The job of vcl is to fix your compiler. C++ is not just a language; the standard also includes an extensive library of classes and functions, which make ISO C++ a powerful and useful tool for building computer programs. Unfortunately, few C++ compilers available in 2001 have a bug-free implementation of the standard, so we have to supply our own bug fixes.
To give an example of the type of problems that vcl fixes, here are a few interpretations from the standard which have been observed in some well known vendors' libraries. Many are entirely within the letter of the law, but remain prone to introduce confusion.
On one compiler, <iostream>
and <iostream.h>
refer to
entirely different stream libraries, which may not be linked together.
Therefore every file in the program must use the same header. For us, the
<iostream>
version is appropriate, but of course, not all of the
unix compilers support its use. The solution is for every vxl program
to include <vcl_iostream.h>
. In this way, we can maintain
consistency between the many compilers, and if we ever do need to use
another stream library, we can make the switch in one place.
Thus rule one is
Wherever you wish to include standard header
<foo>
, you should include<vcl_foo.h>
instead.
Some compilers place STL classes such as vector<>
and string
in
namespace std::
, some don't. Yet others place them there, but do not
implement namespaces properly. Therefore, it is very difficult to write
portable code because sometimes one must say std::vector
, sometimes
one must use vector
. Again, we need a way which works on all
systems. We could try to insert using namespace std;
or using
std::vector
commands throughout the program, but (a) this is not
considered good C++, and (b) it doesn't work anyway.
The low-tech solution is simply to prefix each standard identifier with
vcl_
, so that vcl_vector
works everywhere. And this is
what vxl does, when you include <vcl_vector.h>
. Thus, safe
programmers prefix everything in the standard library with
vcl_
.
Wherever you wish to use standard class or function
foo
, you should writevcl_foo
instead.
This may seem excessive, but one gets used to it very quickly, and it quickly indicates to novice C++ programmers which functions are from the standard library. You might think that the designers of vxl would have been clever enough to avoid the vcl_ prefix by using fancy compiler flags, and many #defines. However, that way lies madness--trying to confuse a C++ compiler always rebounds on one.
Also, when time comes when all compilers will implement ANSI STL classes
in a consistent way, it's very easy to `perl away' the vcl_
prefixes,
or replace them with std::
; it's much more difficult, if not impossible,
to insert std::
prefixes when there are no vcl_
prefixes.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This program is exemplary. It shows how every identifier in the ISO
library has been prefixed by vcl_
. It may look like extreme
overkill, but it works, and can be made to work on all compilers we've
seen.
#include <vcl_string.h> // string #include <vcl_iostream.h> // cout #include <vcl_vector.h> // vector #include <vcl_algorithm.h> // copy #include <vcl_iterator.h> // ostream_iterator int main() { vcl_vector<vcl_string> strings; strings.push_back("Hello, "); strings.push_back("World."); vcl_copy(strings.begin(), strings.end(), vcl_ostream_iterator<vcl_string>(vcl_cout)); return 0; } |
The alternative is somewhat scary. It begins
#if defined(_WIN32) || (defined(__SUNPRO_CC) && (__SUNPRO_CC + 0) >= 0x500) #include <string> #else #include ... |
This document has little more to say about the contents of VCL--a book on
C++ should describe it better than we can. However, it is important to
note that nothing more can go in there. If it's not in the standard, it's
not in VCL. Remember, VCL is full, nothing else can go in there. It
cannot for example be "helpfully" modified, Microsoft-style, to send
standard error to a window (but see also vul_redirector
).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The C++ ISO standard library headers include the functionality of the C ISO standard
library headers. For example, the declarations found in <stdlib.h>
can be
found in <cstdlib>
but in namespace std::
. This means that functions
like printf()
should be called using std::printf()
instead; omitting
the std::
is wrong and won't work if the compiler is truly conforming. The
exception to this (see [C.2.3]
in the standard) is those names from ISO C
which are actually macros. The following is an incomplete list:
For example, the following code is the correct way to use C streams in VXL:
#include <vcl_cstdio.h> #include <vcl_cassert.h> void f(char const *file_name) { assert(file_name); vcl_FILE *fp = vcl_fopen(file_name, "r"); if (! fp) { vcl_fprintf(stderr, "failed to open %s for reading.\n", file_name); vcl_abort(); } ... other stuff ... } |
Note that it uses assert
, stderr
and not vcl_assert
, vcl_stderr
even though it uses vcl_fprintf
, vcl_abort
. This may seem complicated and
hard to remember, but it isn't the fault of VCL. If your compiler were strictly conformant
you would still have to use std::fprintf
and you couldn't use std::stderr
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Eventually the answer to this will be "all parts" but until compilers catch up with the language standard, the answer is "all but the following":
Of course, if you are just using VXL for your own purposes you may use whatever C++ constructs you like, you just can't put them in the core VXL libraries.
The justification for banning certain things in core libraries is to encourage the adoption of the core by reducing the possibility of porting problems. The justification for allowing it for Level 2 and greater libraries is that they are really pretty useful and hard to do without in more complex libraries than those in the core (e.g. RTTI for doing things like strategy patterns, or managing polymorphic class trees).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
In C++, template instantiation is done by the compiler. In real life, it doesn't work as the standard says. In brief here is how template instantiation is supposed to work:
To understand the implications of this (and the meaning of "exported") let's consider the following program, composed of two "translation units" (i.e. files):
// matrix.h template <typename T> struct matrix { matrix(int, int); ... }; |
The class template matrix<>
just declared is defined in
// matrix.txx #include "matrix.h" template <typename T> matrix<T>::matrix(int m, int n) { ... } |
Finally we refer to the matrix class in a little program:
// program.cxx #include "matrix.h" int main() { matrix<double> P(3, 4); ... return 0; } |
The program is ill-formed because the matrix<double>
must be
instantiated before its use in program.cxx
, but the definition
isn't in scope at that point.
One way to fix this is to explicitly instantiate the required template in
some source file and make sure to compile that source file first.
Another is to include the definition of the template in the header file.
A third solution is to put the keyword export
in front of the declaration
of matrix<T>
, which makes it possible to implicitly instantiate
matrix<double>
even when the definition is not in scope.
Unfortunately, there are at the time of writing (April 2001) no compilers
which understand and implement export
so we are currently limited to
using two kinds of templates:
vcl_vector<vcl_pair<int, vcl_string> >
.
Templates/
directories in the source tree and include things like vnl_svd<T>
which only need to be instantiated for a handful of types anyway.
Now, it gets worse. For various reasons it is sometimes advantageous to turn
off automatic instantiation of the first kind of template. This is only really
the case for some architectures but if you are unlucky enough to be using one
of them, you also have to explicitly instantiate your STL container classes
and algorithms in the Templates/
directories. [You should consider skipping
the rest of this section until you actually have a template problem. Don't read it
just for pleasure.] To make it easier to do
this, and to make sure it works on all platforms, explicit instantiation is
done using preprocessor macros. The macro used to instantiate a class or function
is obtained by capitalizing the name (of that class or function) and appending
_INSTANTIATE
. For example, here is how to instantiate a
vcl_map<int, X>
where X
is the name of some class:
// Templates/vcl_map+int.X-.cxx #include "X.h" // declaration of class X. #include <vcl_map.txx> // the instantiation macro lives here. VCL_MAP_INSTANTIATE(int, X, vcl_less<int>); |
and here is how to instantiate vcl_vector<X *>
:
// Templates/vcl_vector+X~-.cxx struct X; // forward declare the class. #include <vcl_vector.txx> VCL_VECTOR_INSTANTIATE(X *); |
The naming convention for such files is as follows:
VSL_VECTOR_IO_INSTANTIATE( vcl_vector< vcl_pair< unsigned int, vxl_int_64 > > )
_INSTANTIATE
and convert the Macro name to lowercase, e.g.
vsl_vector_io( vcl_vector< vcl_pair< unsigned int, vxl_int_64 > >)
()
to angle brackets <>
e.g.
vsl_vector_io< vcl_vector< vcl_pair< unsigned int, vxl_int_64 > > >
unsigned *
' with 'u*
', i.e. 'unsigned int
'
with 'uint
', etc. e.g.
vsl_vector_io< vcl_vector< vcl_pair< uint, vxl_int_64 > > >
vcl_
' prefixes from fixed width types. e.g.
vsl_vector_io< vcl_vector< vcl_pair< uint, int_64 > > >
<
' with '+
', '>
' with
'-
', and ',
' with '.
' e.g.
vsl_vector_io+vcl_vector+vcl_pair+uint.uint---
If you are using the build system that comes with VXL and you aren't using
implicit instantiation you should put such instantiations in the
Templates/
directory or you will be stuffed.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
First of all a definition: Assertions include anything that acts like an assert(). They check for some error condition that should not occur if the code is working correctly(1). They are there to detect broken code. The fact that they abort rather than do something more graceful is irrelevant because the program is already broken. Typical things to check for include array bounds violations, container size mismatches, invalid function parameters. The following things should not be considered as assertions; invalid user input, file input failure; users are too good at messing these things up, and should be treated sympathetically.
When putting an assertion in one of the vxl libraries, you
should make sure that it can be turned off using NDEBUG
.
This is the intention of the NDEBUG macro, and is very useful for
time-critical code.
The easiest way to do this is using the assert()
macro. If
you want to print out a more useful error message you could try
#include <vcl_iostream.h> #include <vcl_cstdlib.h> int f() { ... #ifndef NDEBUG if (vcl_sqrt(4.0) < 1.0) { vcl_cerr << "There is something very wrong with your" << "vcl_sqrt() function" << vcl_endl; vcl_abort(); } ... return 0; } |
However you should bear in mind the extra compilation overhead compared
to just #include <vcl_cassert.h>
.
If you want finer control you can add extra control macros.
Indeed in the case of time-critical code, you are encouraged to provide
this extra control. You can have the default (i.e. when the control macro
is undefined) either include, or not include, the assertion.
In any case, you should ensure that defining NDEBUG
will
override your specialist macros, and turn off all assertions.
For example,
#include <vcl_iostream.h> #include <vcl_cstdlib.h> int f() { ... #if (!defined NDEBUG) && (defined I_AM_PARANOID_ABOUT_VCL_SQRT) if (vcl_sqrt(4.0) < 1.0) { vcl_cerr << "There is something very wrong with your" << "vcl_sqrt() function" << vcl_endl; vcl_abort(); } ... return 0; } |
Of course, you should also document the effect of your macro in the function Doxygen markup (or class level if appropriate.)
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Do not forward declare classes in vcl. For example,
class vcl_string; // This is not allowed. std::string is a typedef. class my_class { .. |
In this case you should just include <vcl_string.h>. In the case of stream stuff, there is an include file of forward declarations that will work.
#include <vcl_iosfwd.h> |
General rule: never forward declare vcl_something with "class vcl_something;" but either `#include <vcl_something_fwd.h>' or `#include <vcl_something.h>'
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This document was generated on May, 1 2013 using texi2html 1.76.