Re: namespace questions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Harvey,

> 1. What's the meaning of "static" when used in a namespace?

Same as when not used in a namespace.

// The i is local (internal linkage) to this translation unit.
static int i = 3; // Not suitable for a header.

> 2. What's the difference between "const" and "extern const" when used
in a namespace?

Linkage, and DECLARATION versus DEFINITION.  Don't violate ODR.

// The i is implicitly static (internal linkage) to this translation unit.
// (Suitable for having in a header file.)
int const i = 3; // Implicitly static.

// The i has external linkage.  It is DECLARED, but not DEFINED here.
// (Suitable for having in a header file.)
extern int i;

// The i has external linkage, it is DEFINED here.
// (Not suitable for having in a header file.)
extern int const i = 3;

// The i has external linkage, it is first DECLARED, then DEFINED.
extern int const i; // Suitable for header file.
int const i = 3; // Not suitable for header file.

// This (as is) is an error, and won't compile.
int const i;

> 3. Are there any tricks/best practices to know when using a namespace (defined
in its own header) in multiple source files?

Use header guards.

Don't put trailing semi-colons after the closing brace.

Don't violate ODR.

Never do a "using namespace FOO" at the global level.

Try to avoid doing a "using namespace FOO" within a namespace, unless you
have a good reason to do so (e.g., using namespaces as a mechanism for
versioning, within your namespace).  Consider "using FOO::thing;" instead of
wholesale "using namespace FOO".

Keep in mind that the anonymous namespace does not mean that the linkage is
internal.  (That may be getting fixed in the revised forthcoming C++0x
standard...?)

If you are making an SDK, I urge you to use your public headers as a
"barrier" to your implementation (i.e., encapsulation).

That means, don't put implementation in the public headers.  Avoid inlined
functions (including avoiding implicit synthetic inline default
constructors, copy constructors, destructors, assignment operators),
consider enum and constants long-and-hard if you want them to be fixed
feature of your SDK landscape that can "never" change, or if you want the
flexibility to change your SDK over time and making those constants be
externally linked symbolics.

I recommend this naming convention, as "food for thought":
.h - C++ header file
.cpp - C++ source file
.h.h - C++ header-header file
.i.h - C++ inline file, which is #include'd by a .h file
.m.h - a macro "header" file, which is used in a special way

An example of a .m.h file:
------ Foo.m.h ------
MACRO(alpha, void, (char, char), "obsolete, do not use")
MACRO(beta, void, (char const*), "print a string")
MACRO(gamma, void, (char*, size_t), "initialize a buffer")
#undef MACRO

------ Foo.h ------
...
#define MACRO(fnName, fnReturnType, fnParameters, fnDescription) \
 fnReturnType fnName fnParameters
#include "Foo.m.h"
...
enum Trigger
{
#define MACRO(fnName, fnReturnType, fnParameters, fnDescription) \
 key ## fnReturnType
#include "Foo.m.h"
};
...
// This kind of function shouldn't be in a header, just for example.
inline char const* TriggerName(Trigger inTrigger)
{
  switch(inTrigger)
  {
#define MACRO(fnName, fnReturnType, fnParameters, fnDescription) \
  case key ## fnName : return fnDescription;
#include "Foo.m.h"
  default: break;
  }
  return "unknown";
}

End of example.

A "header header" file is one that forward declares classes, but does not
provide the declaration.  Used as an far better alternative than source code
doing its own forward declares, and maybe getting them wrong when a class
changes from a "regular" class to, say, a template class and typedef.  They
are intended to reduce churn when making changes, especially important for
large projects.  Some other things can go in a "header header" as well, such
as enums and certain kinds of typedefs.

See "Large-Scale C++ Software Design" by John Lakos
http://www.amazon.com/dp/0201633620/

My critique on your code sample below.  Your mileage may vary.

Sincerely,
--Eljay

- - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// g++ -Wall namespace.cc main.cc

// The following code can also be compiled as-is in one file.

////// namespace.hh   //////
#ifndef NAMESPACE_HH //<--GOOD
#define NAMESPACE_HH //<--GOOD

namespace Name
{
   static const char charA = 'A'; //<--BAD
   const char charB = 'B'; //<--GOOD
   extern const char charC; //<--GOOD
   extern char charD; //<--GOOD[1]
}; //<--REMOVE superfluous semicolon

#endif // NAMESPACE_HH


////// namespace.cc   //////
//#include "namespace.hh"

const char Name::charC = 'C'; //<--GOOD
char Name::charD = 'D'; //<--GOOD


////// main.cc   //////
//v--CONSIDER using C++ <iostream> I/O instead of C <cstdio>.
#include <stdio.h> //<--BAD, use #include <cstdio> for C++
//#include "namespace.hh"

int main()
{
   using std::printf; //<--ADDED
   printf("charA = %c\n", Name::charA);
   printf("charB = %c\n", Name::charB);
   printf("charC = %c\n", Name::charC);
   printf("charD = %c\n", Name::charD);

   // v--AVOID gratuitous parens.  return is not a function.
   // v--CONSIDER using <cstdlib> return EXIT_SUCCESS; instead of return 0;
   return(0);
} 
                   
[1] The "extern" is redundant.  My personal preference is to explicitly
specify the (redundant) extern, since I think it helps the maintenance
developer.  Just as having a subclass specify "virtual" on an overridden
method is redundant, I prefer specifying it.  (I wish C++ has a *required*
"override" keyword for overriding a virtual method.  But that's a different
story, and is not what C++ is.  In an alternate reality...)


[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux