[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3. vcl: C++ compatibility

Chapter summary: When you want a std::string, use vcl_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 write vcl_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] [ ? ]

3.1 Example

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] [ ? ]

3.2 Macros and standard C++

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] [ ? ]

3.3 Which parts of standard C++ may be used in VXL?

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] [ ? ]

3.4 Template instantiation

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:

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:

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] [ ? ]

3.5 Use of Assertions (Developer Topic)

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] [ ? ]

3.6 Notes


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3.6.1 Forward Declaration of vcl Classes

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.