6 Scope

Use namespaces to avoid name collisions

6.1 All Axom code must be included in the project namespace ‘axom’; e.g.,:

namespace axom {
     // . . .
}

6.2 Each Axom component must define its own unique namespace within the “axom” namespace. All contents of each component must reside within that namespace.

Use namespaces to hide non-API code in header files

6.3 Code that must be appear in header files (e.g., templates) that is not intended to be part of a public interface, such as helper classes/structs and methods, should be placed in an internal namespace.

Common names for such namespaces include ‘internal’ (for implementations used only internally) and ‘detailed’ (for types, etc. used only internally). Any reasonable choice is acceptable; however, the choice must be the same within each Axom component.

Note that declaring helper classes/structs private within a class definition is another good option. See Hide nested classes when possible for details.

Use ‘unnamed’ namespace for hiding code in source files

6.4 Classes/structs and methods that are meant to be used only internally to a single source file should be placed in the ‘unnamed’ namespace to make them invisible outside the file.

This guarantees link-time name conflicts will not occur. For example:

namespace {
   void myInternalFunction();
}

Apply the ‘using directive’ carefully

6.5 The ‘using directive’ must not be used in any header file.

Applying this directive in a header file leverages a bad decision to circumvent the namespace across every file that directly or indirectly includes that header file.

Note

This guideline implies that each type name appearing in a header file must be fully-qualified (i.e., using the namespace identifier and scope operator) if it resides in a different namespace than the contents of the file.

6.6 The ‘using directive’ may be used in a source file to avoid using a fully-qualified type name at each declaration. Using directives must appear after all “#include” directives in a source file.

6.7 When only parts of a namespace are used in an implementation file, only those parts should be included with a using directive instead of the entire namespace contents.

For example, if you only need the standard library vector container form the “std” namespace, it is preferable to use:

using std::vector;

rather than:

using namespace std;

Use access qualifiers to control class interfaces

6.8 Class members must be declared in the following order:

  1. “public”

  2. “protected”

  3. “private”

That is, order members using these access qualifiers in terms of “decreasing scope of visibility”.

Note

Declaring methods before data members is preferred because methods are more commonly considered part of a class interface. Also, separating methods and data into their own access qualified sections usually helps make a class definition clearer.

6.9 Class data members should be “private”. The choice to use “public” or “protected” data members must be scrutinized by other team members.

Information hiding is an essential part of good software engineering and private data is the best means for a class to preserve its invariants. Specifically, a class should maintain control of how object state can be modified to minimize side effects. In addition, restricting direct access to class data enforces encapsulation and facilitates design changes through refactoring.

Use ‘friend’ and ‘static’ rarely

6.10 “Friend” declarations should be used rarely. When used, they must appear within the body of a class definition before any class member declarations. This helps make the friend relationship obvious.

Note that placing “friend” declarations before the “public:” keyword makes them private, which preserves encapsulation.

6.11 Static class members (methods or data) must be used rarely. In every case, their usage should be carefully reviewed by the team.

When it is determined that a static member is needed, it must appear first in the appropriate member section. Typically, static member functions should be “public” and static data members should be “private”.

Hide nested classes when possible

6.12 Nested classes should be private unless they are part of the enclosing class interface.

For example:

class Outer
{
   // ...
private:
   class Inner
   {
      // ...
   };
};

When only the enclosing class uses a nested class, making it private does not pollute the enclosing scope needlessly. Furthermore, nested classes may be forward declared within the enclosing class definition and then defined in the implementation file of the enclosing class. For example:

class Outer
{
   class Inner; // forward declaration

   // use name 'Inner' in Outer class definition
};

// In Outer.cpp implementation file...
class Outer::Inner
{
   // Inner class definition
}

This makes it clear that the nested class is only needed in the implementation and does not clutter the class definition.

Limit scope of local variables

6.13 Local variables should be declared in the narrowest scope possible and as close to first use as possible.

Minimizing variable scope makes source code easier to comprehend and may have performance and other benefits. For example, declaring a loop index inside a for-loop statement such as:

for (int ii = 0; ...) {

is preferable to:

int ii;
...
for (ii = 0; ...) {

Beyond readability, this rule has benefits for thread safety, etc.

Note

Exception: When a local variable is an object, its constructor

and destructor may be invoked every time a scope (such as a loop) is entered and exited, respectively.

Thus, instead of this:

for (int ii = 0; ii < 1000000; ++ii) {
   Foo f;
   f.doSomethingCool(ii);
}

it may be more efficient to do this:

Foo f;
for (int ii = 0; ii < 1000000; ++ii) {
   f.doSomethingCool(ii);
}

6.14 A local reference to any item in the global namespace (which should be rare if needed at all) should use the scope operator (“::”) to make the fact that it resides in the global namespace clear.

For example:

int local_val = ::global_val;