Suggested extension to C++: static namespaces

R

Rennie deGraaf

From time to time, I need to define a class that isn't part of the
public API of whatever library or module that I'm developing, but still
needs to be in the header file because it's used by some class that is
in the public API. In Java, I'd use something like /package/ visiblity
on such classes within the package I'm developing, and use /public/
visibilty on classes in the public API. In C++, the best I've been able
to come up with is something like this:

namespace SomeNS
{
class _PseudoNS
{
private:
_PseudoNS() {} // _PseudoNS is non-instantiable
class ImplementationDetail {/* ... */};
friend class PublicAPI;
public:
class APIObject : public ImplementationDetail {/* ... */};
};
typedef _PseudoNS::APIObject APIObject;
class PublicAPI
{
private:
std::vector<APIObject> internalData;
/* ... */
};
}

This works, but it isn't a particularly good solution. Since _PseudoNS
isn't really a namespace, you can say "using _PseudoNS::APIObject";
that's why I have the typedef in there. If APIObject is a template,
rather than a class, then you can't typedef it (I'm aware that this
problem is already scheduled to be fixed in C++0x), and you have to
refer to it as "_PseudoNS::APIObject" all the time. Also, since
_PseudoNS is really a class and PublicAPI has access to its constructor,
it's possible for someone to accidentally instantiate it from within
PublicAPI, which is (or at least, should be) a totally meaningless
operation. The compiler will also have to compile _PseudoNS as a class,
which wastes time and storage space. What I'd like to see would be
extensions to the language to support something like this:

static namespace SomeNS
{
private:
class ImplementationDetail {/* ... */};
public:
class APIObject : public ImplementationDetail {/* ... /*};
class PublicAPI
{
private:
std::vector<APIObject> internalData;
/* ... */;
};
}

A /static namespace/ (maybe /const namespace/ would be a better term?)
would then be a namespace that cannot be re-opened. /Private/ symbols
within it would be visible only to other members of the namespace, and
public symbols would be accessible from outside. I don't think that
there would be any meaning for a /protected/ visibility class.

Of course, an extension like this could throw a few wrenches into other
stuff. One could probably make a case that if this was acceptable, then
you should also be allowed to do this:

static namespace SomeNS
{
friend namespace SomeOtherNS;
/* ... */
}

Any thoughts?

Rennie deGraaf
 
O

Old Wolf

Rennie said:
From time to time, I need to define a class that isn't part of the
public API of whatever library or module that I'm developing, but still
needs to be in the header file because it's used by some class that is
in the public API.

You should refer to it with a pointer or reference in the header file.
Then you can forward-declare the class in the header, and save
the class definition for the cpp file.
 
R

Rennie deGraaf

Old said:
Rennie deGraaf wrote:




You should refer to it with a pointer or reference in the header file.
Then you can forward-declare the class in the header, and save
the class definition for the cpp file.

AFAIK, you can't inherit from a forward-declared class. You need the
whole class declaration in scope before you can inherit from it.

Code like this:

// header.hpp
class ImplementationDetail;
class PublicAPI : private ImplementationDetail {};

// implementation.cpp
#include "header.hpp"
class ImplementationDetail {};

is invalid. So, you need to put ImplementationDetail in the header
before PublicAPI. This allows users to play with it, which may be a Bad
Thing if, for instance, its constructor has side effects.

Rennie deGraaf
 
S

Shezan Baig

Rennie said:
A /static namespace/ (maybe /const namespace/ would be a better term?)
would then be a namespace that cannot be re-opened. /Private/ symbols
within it would be visible only to other members of the namespace, and
public symbols would be accessible from outside.


A class/struct can already be used to achieve this:

struct SomeNS {
private:
static void implementation(int a, double b);

public:
static void interface(int a);
};


int var;
SomeNS::interface(var);

Hope this helps,
-shez-
 
O

Old Wolf

Rennie said:
AFAIK, you can't inherit from a forward-declared class. You need the
whole class declaration in scope before you can inherit from it.

Code like this:

// header.hpp
class ImplementationDetail;
class PublicAPI : private ImplementationDetail {};

// implementation.cpp
#include "header.hpp"
class ImplementationDetail {};

is invalid. So, you need to put ImplementationDetail in the header
before PublicAPI. This allows users to play with it, which may be a Bad
Thing if, for instance, its constructor has side effects.

I really would recommend you don't derive your class from
ImplementationDetail. This violates the spirit of interface/
implementation separation: if you add a variable to the
ImplementationDetail, then it should NOT affect any client
using your class.

In your case, the clients would all have to recompile everything,
and your 'static namespace' idea does nothing to address this issue.

Make the implementation detail a private reference/pointer member
instead.
 
H

hacker++

Rennie said:
AFAIK, you can't inherit from a forward-declared class. You need the
whole class declaration in scope before you can inherit from it.

Code like this:

// header.hpp
class ImplementationDetail;
class PublicAPI : private ImplementationDetail {};

// implementation.cpp
#include "header.hpp"
class ImplementationDetail {};

is invalid. So, you need to put ImplementationDetail in the header
before PublicAPI. This allows users to play with it, which may be a Bad
Thing if, for instance, its constructor has side effects.

You could make all the member functions of ImplementationDetail
protected so only derived classes can use it.

Atul
 
R

Rennie deGraaf

Shezan said:
A class/struct can already be used to achieve this:

struct SomeNS {
private:
static void implementation(int a, double b);

public:
static void interface(int a);
};


int var;
SomeNS::interface(var);

Hope this helps,
-shez-

That's exactly the workaround that I suggested in my original post. But
it's just a workaround, not a perfect solution. That's why I think that
a language extension would be useful. See the original post for details.

Rennie deGraaf
 
S

Shezan Baig

Rennie said:
That's exactly the workaround that I suggested in my original post. But
it's just a workaround, not a perfect solution. That's why I think that
a language extension would be useful. See the original post for details.


What exactly is not "perfect" about it? The fact that you're using the
'struct' keyword instead of a 'namespace' keyword? You asked for a
namespace that couldn't be re-opened and had visibility options --
that's exactly what a 'struct/class' is. Make constructors private if
you feel that you really have to, but I see no need to extend the
language for something like this.

-shez-
 
R

Rennie deGraaf

Shezan said:
What exactly is not "perfect" about it? The fact that you're using the
'struct' keyword instead of a 'namespace' keyword? You asked for a
namespace that couldn't be re-opened and had visibility options --
that's exactly what a 'struct/class' is. Make constructors private if
you feel that you really have to, but I see no need to extend the
language for something like this.

-shez-

A struct/class is a description of a type of object, which enforces a
particular interpretation of a unit of memory where data can be stored.
They have meaning at tun-time, so the compiler needs to output
information about them. The fact that other structs/classes can be
declared inside a struct/class is purely syntactic sugar. Programs
don't care if a particular object is of a globally-defined class, or a
locally defined one (unless your language checks access restrictions at
run-time).

A namespace is a logical grouping of symbols, which only has meaning at
compile-time. It's a much simpler structure than a class (it can't be
instantiated, it doesn't have ctors/dtors, it can't be inherited, etc),
and works a little differently than a class/struct. See the 2nd
paragraph of my original post for details.

I guess another way of achieving the same thing would be to allow
statements like
using Foo::Bar;
or
using namespace Foo;
where Foo is a class. But then, the compiler still needs to check if
Foo is actually instantiated anywhere before it can decide if it's
really a class or a namespace that's declared as a class. The whole
point of the separate keywords is to let programmers make this
distiction. Checking if classes are instantiated before deciding what
to output should be a compiler optimization, not required functionality.

Rennie deGraaf
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top