Other header file issues

Avoiding 'using' directives from header files makes for uglier header files (harder to read with type names that can take many columns of characters) as in 'std::map<std::string, std::vector<int> >' vs 'map<string, vector<int> >'. A possible (hackish) solution is suggested at the bottom here.

If a class' interface uses some STL template class, then the #include for it has to be in the .h file and the .C file. Having #include <string> in the .h file doesn't mean we're free not to #include that same STL template class header in the corresponding .C file. Indeed the STL string class may be part of the interface and also, separately, part of the implementation of the class.

I think there may even be some industry adopted practice for this..within a .C file, #include directives could benefit from being ordered such that the most general (widest scope) appear at the bottom of the list (clib and stl headers) while the most specific to the current source appear at the top. In addition, with a group of includes from the same (portion of a) product, it helps to alphabetize the includes in the list. Finally, any 'using' directives should go AFTER all #include directives.

So, for example, in a VisIt database plugin that uses a bunch of avt, utility, vtk, hdf5. mpi, and stl includes, it might look like...

#include avtAAA...
#include avtABB...
        
// These are arguably more 'general' parts of VisIt than avt
#include StringHelpers.h
#include DebugStream.h
        
// Order of vtk relative to HDF5 is probably not all that significant
#include vtkDataArray.h
#include vtkFloatArray.h
        
#include hdf5.h
        
// HDF5 makes use of MPI, so I place it 'more general' than HDF5
#include mpi.h
        
// Order of c headers relative to c++ headers may not be too critical
// but since c++ sort of builds upon c, I'd say c is 'more specific' than c++
#include ctypes.h
        
#include <string>
#include <vector>
        
// All using directives after all include directives
using std::vector;

Some rationals for this organization are...

We have more control over the contents of our headers or those of libraries we write and less control over the contents of others. So, placing the files we have less control over further down in the list limits the impact of any issues they may cause and allows us to address any issues by re-structuring the includes that we do have control over that come before them. Whew! that was long-winded.

For long lists, alphabetizing makes it easier to find header files.

To avoid multiple inclusion, we use the standard strategy...

#ifndef AVT_SSE_H
#define AVT_SSE_H

class avtSSE {
};

#endif

But, when creating new classes by copying an existing class header and then changing names, the macro symbol used to avoid multiple inclusion can be easily missed in the name change since its spelling and case are totally different. It would be nicer to use a convention like...

#ifndef avtSSE_H_INCLUDED
#define avtSSE_H_INCLUDED

class avtSSE {
};


#endif

So replacing avtSSE with avtFOO will catch everything, including the all important duplicate header inclusion symbol.

To address 'uglier' header file structure due to 'std::' everywhere, one solution would be to have a pair of headers that define (and undefine) macros to make a header file a little more readable...

In one file, call it 'short_stl_names.h', we could have...

#ifdef vec
#error LOOKS LIKE THERE IS A MISSING INCLUDE FOR undef_short_stl_names
#endif
#define vec std::vector
#define map std::map
#define str std::string
#define deq std::deque

In the other file, 'undef_short_stl_names.h', we'd have...

#undef vec
#undef map
#undef str
#undef deq

And any given header could, optionally, #include <short_stl_names.h> as the LAST include file at the beginning and #include <undef_short_stl_names.h> at the end to avoid leakage of these symbols anywhere else. The #error statement in the first header of the pair will help to prevent cases where someone forgets to add the pairing include to undef these symbols.