Topic: Proposals for a better C++
Author: AlbertoBarbati@libero.it (Alberto Barbati)
Date: Sat, 18 Sep 2004 10:09:26 GMT Raw View
Helium wrote:
>
> What about
>
> class my_class
> {
> class
> {
> /* implementation details */
> } implementation_details;
>
> /* public interface */
> };
>
> Should work.
>
Sure that's better, but it's not perfect either, because implementation
methods cannot access "public" members without recurring to ugly and
potentially non-portable casts on the this pointer.
Alberto
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: francis@robinton.demon.co.uk (Francis Glassborow)
Date: Sat, 18 Sep 2004 17:07:01 GMT Raw View
In article <jpL2d.9057$zF3.233377@twister2.libero.it>, Alberto Barbati
<AlbertoBarbati@libero.it> writes
>Randy Maddox wrote:
>> Now everything inside the nested unnamed namespace is private to
>> namespace X.
>>
>
>That would work inside a cpp file, but here we are talking about
>namespaces in a header file, so your remark is irrelevant.
But if something should be private to a namespace, why would I put it in
a header file? Unlike classes, I can hide 'private' things in namespaces
by making them invisible outside an implementation file and nothing else
needs to know about them.
The only case where this does not work is if you want some function
overload to be found but cause a compile time failure.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: AlbertoBarbati@libero.it (Alberto Barbati)
Date: Mon, 20 Sep 2004 03:25:26 GMT Raw View
Francis Glassborow wrote:
> In article <jpL2d.9057$zF3.233377@twister2.libero.it>, Alberto Barbati
> <AlbertoBarbati@libero.it> writes
>
>> Randy Maddox wrote:
>>
>>> Now everything inside the nested unnamed namespace is private to
>>> namespace X.\
>>>
>>
>> That would work inside a cpp file, but here we are talking about
>> namespaces in a header file, so your remark is irrelevant.
>
>
> But if something should be private to a namespace, why would I put it in
> a header file? Unlike classes, I can hide 'private' things in namespaces
> by making them invisible outside an implementation file and nothing else
> needs to know about them.
>
> The only case where this does not work is if you want some function
> overload to be found but cause a compile time failure.
>
Templates definitions cannot be hidden in an implementation file so you
have to put them in the header file even you want them to "private".
Just count how many Boost libraries uses the "detail namespace" idiom to
see what I mean.
Sure, if you have a compiler that implements the export keyword, that
may no longer be true, but until this feature is wide available...
Alberto
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: RTeodorescu@gmail.com (Radu Teodorescu)
Date: Wed, 15 Sep 2004 17:34:27 GMT Raw View
Alberto Barbati wrote:
> Radu Teodorescu wrote:
> >
> > Private namespaces
> > ------------------
> >
> > [snips]
> >
> > What happens if the user creates an typearray with the code "typedef
> > TA::TypeArray<int, NullType, char> MyTypeArray"? The best approach for
> > the designer of the typearrays will be to state "this thing shouldn't
> > happen". But unfortunately, according to the current standard, there
> > is no way of stopping such things to happen (we incapsulate NullType
> > and TypeArray in a class because TypeArray is specialized, and
> > specializations are allowed only at namespace scope). This shows that
> > the current standard of C++ lacks here, and leads us to the conclusion
> > that the new C++ standard must implement a method of declaring private
> > namespaces.
> >
> > One idea that is to have public and private keywords for namespaces as
> > we have for classes (it would be very interesting to introduce
> > "namespace inheritance" and to use protected clause too, but this
> > would be the subject of a different paper). This will lead to very
> > good results. Let's show in an example a possible way of using this:
> >
>
> A typical idiom to solve this kind of issues is to use a nested
> namespace with a self-descriptive or deliberately ugly name (for example
> the Boost library uses namespace "detail"). Library users are warned
> that referring to any symbol declared in the detail namespace is looking
> for trouble. This measure is good enough to avoid any unintended use of
> such symbols. Maybe it may seem not enough for you, but adding a private
> keyword provides no better security: if I want to shoot myself on my
> foot, I can always edit the header file and comment out the line with
> the private keyword!
>
> IMHO, adding public and private keyword at namespace scope does not add
> any extra value.
Do you think that the fact that classes supports public and private
keywords are a case of "shoot myself on my foot"? Of course, we can
leave without them very well: just put all class members into public
section. But that won't be a good argument that these keywords are
useless for classes. You can always comment all the private keywords
in a class too.
> >
> > The undeclare and replace_declaration keywords
> > ----------------------------------------------
> >
>
> > By using the extern keyword, we can say to the compiler "this
> > declaration exists somewhere else, we can use it", but there is no way
> > that you can tell the compiler "this declaration exists, but don't let
> > anyone see it". It will be a good idea to have in the new C++ standard
> > an undeclare keyword that will un-declare a certain declaration. In
> > our typearray example we can write an "undeclare class NullType;"
> > directive at the end of the TA namespace, to solve our problem.
>
> "undeclare" might have some use, but frankly I don't feel the urge to
> have such a feature. I would hate the proliferation of undeclarations.
> Anyway, until we have the dreaded preprocessor, one can easily undefine
> a symbol by replacing it with a macro.
Yes, you can do the macro trick, but you can do this in the
preprocessing phase, and you won't be able to use the features of the
compiler. For example, how can you undefine a template specialization?
I can find some more examples and more complex that would show that
these two keywords would be usefull. For example you can even create
some kind of "compile time variables", meaning that you put different
data into the same entity.
>
> However, the hack you describe in the following to redeclare symbols is
> even worse than the use of preprocessing macros. I strongly object to
> its usefulness and necessity.
>
No comment.
> > Let's have another example: one has found a better method of
> > implementing strcpy, and he wants every source file that includes a
> > certain header to use his function instead of using the default
> > function. In this case he probably wants to write in his header:
> >
> > undeclare ( char* strcpy(char*, const char*) );
> > char* strcpy(char*, const char*);
>
> Why would one undeclare something and then redeclare it in the same way?
> If I want my program to use my version of strcpy (forgetting, for an
> instant, that it's probably not a good idea), the only thing I need to
> do is:
>
> 1) define strcpy (no need to declare it!!!)
> 2) inform the linker to use my version instead of the one contained in
> the library
>
> Where's the need to undeclare/redeclare? If I include <cstdlib>, strcpy
> gets declared. So what? Does the compiler know to which function the
> strcpy symbol will be bound to? No. It's the linker that performs the
> binding. Everything can be arranged more easily by setting the right
> flags on the linker command line. Why bother the compiler about a linker
> detail?
>
I think you didn't understand completly the examples. The code
fragment you quoted is a non-sense: strcpy is undeclared after the
first line, and the second line would just state that the undeclared
strcpy exists. Maybe a better name for "undeclare" would be
"don_let_anyone_use_this_particular_declaration".
The undeclare statement shouldn't be removed from the compiler's list
of declaration, because the compiler should give an error if the
programmer will use that declaration (or maybe better *the definition
coresponfig to that declaration*).
Why use this instead linking flags? Because sometimes the programmer
wants to redefine the strcpy function only in files "that includes a
certain header", and leave other source files to use the standard
function. Because it's more convinient to change a line of code that
to change the compiler/linker options. Because there is no standard
that shows us what flags should we give to our compilers/linkers to
achieve our goal.
> But, more important: how many times in your programming life did you
> encounter this use case in writing portable code? I can answer according
> to my limited experience: zero.
>
I believe that the purpose of the standard is not to show us how to make
portable code, but to show us *the one and only* coding style. And I
encountered several times the need for such tricks in real-life applications.
> >
> > Template specialization at class scope
> > --------------------------------------
> >
> > Currently in C++ one cannot specialize a template at the class scope,
> > although it can define the template (some compilers - like Visual C++
> > 6.0 - supports this feature). This will break both of the two rules of
> > encapsulation we defined above.
> >
>
> ??? You *can* specialize a template at class scope!! What version of C++
> are you referring to? Sure, if you take VC++ 6.0 as an example of a good
> C++ implementation of template support you are completely out of target.
>
I obviously made a huge mistake here. Please accept my appologies.
> >
> > Typedef templates
> > -----------------
> >
>
> You clearly have never followed the discussions on this newsgroup
> before. This issue has been discussed for a very long time. You can have
> a look at the currently more advanced proposal here:
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1449.pdf
>
Indeed I didin't follow such discussions. My mistake. Sorry for poluting the
discussions.
Best regards,
Radu
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: AlbertoBarbati@libero.it (Alberto Barbati)
Date: Thu, 16 Sep 2004 22:21:12 GMT Raw View
Radu Teodorescu wrote:
> Alberto Barbati wrote:
>
>>A typical idiom to solve this kind of issues is to use a nested
>>namespace with a self-descriptive or deliberately ugly name (for example
>>the Boost library uses namespace "detail"). Library users are warned
>>that referring to any symbol declared in the detail namespace is looking
>>for trouble. This measure is good enough to avoid any unintended use of
>>such symbols. Maybe it may seem not enough for you, but adding a private
>>keyword provides no better security: if I want to shoot myself on my
>>foot, I can always edit the header file and comment out the line with
>>the private keyword!
>>
>>IMHO, adding public and private keyword at namespace scope does not add
>>any extra value.
>
>
> Do you think that the fact that classes supports public and private
> keywords are a case of "shoot myself on my foot"? Of course, we can
> leave without them very well: just put all class members into public
> section. But that won't be a good argument that these keywords are
> useless for classes. You can always comment all the private keywords
> in a class too.
>
I am not objecting the usefulness of the access specifiers. With my
provocation I was just trying to say that the "nested details namespace"
idiom is not much worse (and certainly not less secure) than having the
compiler support private namespace section natively.
public/private keyword are unavoidable for classes because it's the only
reasonable way to "protect" special functions like ctors, dtors, etc.
You can't emulate private section through nesting as you can do with
namespaces. You can write:
namespace my_library
{
namespace implementation_details
{
/* implementation details */
}
/* public interface */
}
and easily obtain a poor-man's private block in a namespace. But the code:
class my_class
{
private:
/* implementation details */
public:
/* public interface */
};
is completely different from
class my_class
{
class implementation_details
{
/* implementation details */
};
/* public interface */
};
The latter cannot replace the former. Sure, you could prefix each
private name with "implementation_detail_" but things would get really
ugly and you could no longer make a member "less private" in a derived
class without changing its name.
>>
>>"undeclare" might have some use, but frankly I don't feel the urge to
>>have such a feature. I would hate the proliferation of undeclarations.
>>Anyway, until we have the dreaded preprocessor, one can easily undefine
>>a symbol by replacing it with a macro.
>
>
> Yes, you can do the macro trick, but you can do this in the
> preprocessing phase, and you won't be able to use the features of the
> compiler. For example, how can you undefine a template specialization?
>
Template are already complex enough... you should try teaching them as I
did! Introducing the possibility to undeclare a template specialization
would promote obscure and unreadable coding styles and may also have
negative impact on the confidence in the C++ community.
> I can find some more examples and more complex that would show that
> these two keywords would be usefull. For example you can even create
> some kind of "compile time variables", meaning that you put different
> data into the same entity.
>
I shudder at the only thought.
> I think you didn't understand completly the examples. The code
> fragment you quoted is a non-sense: strcpy is undeclared after the
> first line, and the second line would just state that the undeclared
> strcpy exists. Maybe a better name for "undeclare" would be
> "don_let_anyone_use_this_particular_declaration".
> The undeclare statement shouldn't be removed from the compiler's list
> of declaration, because the compiler should give an error if the
> programmer will use that declaration (or maybe better *the definition
> coresponfig to that declaration*).
"the definition coresponfig to that declaration"? The compiler does not
have *any* knowledge about which definition corresponds to a certain
declaration. Only the linker knows it. For the compiler, any declaration
will do and you can also have multiple declaration for the same
definition. For example if you write:
int foo();
// (1)
undeclare int foo();
int foo();
// (2)
I expect the compiler states in (1) and in (2) to be exactly identical.
How do you suggest the compiler to implement this particular case to
match your proposed semantic of undeclare?
Anyway, this issue is even bigger, IMHO. The whole C++ is built on one
building block, which is the ODR or One Definition Rule (3.2). If you
destroy the building block, you risk the collapse of the entire
structure. In that case it might be simpler to rewrite the language from
scratch.
Regards,
Alberto
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: RTeodorescu@gmail.com (Radu Teodorescu)
Date: Fri, 17 Sep 2004 08:37:34 GMT Raw View
Alberto Barbati wrote:
> I am not objecting the usefulness of the access specifiers. With my
> provocation I was just trying to say that the "nested details namespace"
> idiom is not much worse (and certainly not less secure) than having the
> compiler support private namespace section natively.
I believe that the "nested details namespace" idiom isn't the reason
why private namespaces aren't in the language, and that when
namespaces were introduced into C++, nobody thought that there can be
some things in a namespace that should be private. (namepsaces were
introduced to solve name conflicts, not to generalize the
encapsulation concept). Of course,
>
> public/private keyword are unavoidable for classes because it's the only
> reasonable way to "protect" special functions like ctors, dtors, etc.
>
> You can't emulate private section through nesting as you can do with
> namespaces. You can write:
>
> namespace my_library
> {
> namespace implementation_details
> {
> /* implementation details */
> }
>
> /* public interface */
> }
>
> and easily obtain a poor-man's private block in a namespace. But the code:
>
> class my_class
> {
> private:
> /* implementation details */
>
> public:
> /* public interface */
> };
>
> is completely different from
>
> class my_class
> {
> class implementation_details
> {
> /* implementation details */
> };
>
> /* public interface */
> };
>
> The latter cannot replace the former. Sure, you could prefix each
> private name with "implementation_detail_" but things would get really
> ugly and you could no longer make a member "less private" in a derived
> class without changing its name.
>
Of course, for classes a one cannot do the same trick, but this was
not the point of my example.
>
> Template are already complex enough... you should try teaching them as I
> did! Introducing the possibility to undeclare a template specialization
> would promote obscure and unreadable coding styles and may also have
> negative impact on the confidence in the C++ community.
>
Peoaple will get used to it. Everyone get confused when start working
with new technologies. The purpose is not to make out of the undeclare
and replace_declaration something that everyone should use to achieve
great result. They are usefull for the library makers, and for people
who can think of the consequences of using them.
> > I can find some more examples and more complex that would show that
> > these two keywords would be usefull. For example you can even create
> > some kind of "compile time variables", meaning that you put different
> > data into the same entity.
> >
>
> I shudder at the only thought.
Do you think that peoaple used to template meta-programming will
consider these undeclare/replace_declaration solutions much more
complicated than the existing meta-programming code? It would just
extend their methods.
>
> > I think you didn't understand completly the examples. The code
> > fragment you quoted is a non-sense: strcpy is undeclared after the
> > first line, and the second line would just state that the undeclared
> > strcpy exists. Maybe a better name for "undeclare" would be
> > "don_let_anyone_use_this_particular_declaration".
> > The undeclare statement shouldn't be removed from the compiler's list
> > of declaration, because the compiler should give an error if the
> > programmer will use that declaration (or maybe better *the definition
> > coresponfig to that declaration*).
>
> "the definition coresponfig to that declaration"? The compiler does not
> have *any* knowledge about which definition corresponds to a certain
> declaration. Only the linker knows it. For the compiler, any declaration
> will do and you can also have multiple declaration for the same
> definition. For example if you write:
>
> int foo();
> // (1)
> undeclare int foo();
> int foo();
> // (2)
>
> I expect the compiler states in (1) and in (2) to be exactly identical.
> How do you suggest the compiler to implement this particular case to
> match your proposed semantic of undeclare?
>
I believe you didn't understand my ideea. Let's try to see the problem
from the point of view of the compiler (I don't know how a compiler
work, so I might make some mistakes here):
Let's assume that the compiler holds a list of all the declaration has
encoutered. In the point (1) the compiler will hold in that list an
internal representation of "int foo()". If you now make a call to
foo(), the compiler will look in the list and use the declaration of
foo().
After the undeclare statement, the compiler will add an undeclared
flag to that declaration that will inform it not to use that
declaration again. After this line in the list there should be an
internal representation for "int foo()" + "undeclared".
If you will try now to declare again, "int foo()" the compiler will
look in its list and see that declaration of "int foo()" already
exists. In this case it does nothing. The compiler list will continue
to hold its internal representation for "int foo()" + "undeclared".
The main point is that the later declaration of "int foo()" *will not
redeclare again* the foo() function.
After point (2) if someone tries to to use the declaration of foo(),
the compiler will look in its list and see that the declaration of
foo() is undeclared. When this happens, a compile error should occur.
This way, once i have undeclared the foo() declaration nobody can use
it anymore.
What should the compiler do in the case of replace_declaration?
Let's say we continue your example code with the folowing code:
int foo2();
replace_declaration( foo, foo2 );
// ......
int result = foo();
Of course, if we want to use replace_declaration foo2() must be
declared in the same manner as foo(). When the compiler see the
replace_declaration line, he must know that every operation performed
on foo() must be performed on foo2(). So, the list of the compiler
should hold the internal representation for "int foo2()" and "int
foo()" + "undeclared" + "Redirect to foo2".
Now, for the call of foo() the compiler, after consulting the list, it
should interpret the line as the folowing code "int result = foo2();".
You see that in this case the declaration of foo() is never used, and
no error is issued.
This way, we can exchange definitions by using declarations.
> Anyway, this issue is even bigger, IMHO. The whole C++ is built on one
> building block, which is the ODR or One Definition Rule (3.2). If you
> destroy the building block, you risk the collapse of the entire
> structure. In that case it might be simpler to rewrite the language from
> scratch.
I don't see why this rule is broken.
Of course, if the undeclare and replace_declaration soultion would
broke that rule, it's better not to think of them, instead of
rewriting C++ from scratch. By the way, I must admit that I would like
very much the ideea of building a new language based on the experience
gathered with C++, and that would avoid all the "scars" of C++ ...
>
> Regards,
>
> Alberto
>
Best wishes,
Radu
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: bekkah@web.de (Helium)
Date: Fri, 17 Sep 2004 16:27:09 GMT Raw View
Alberto Barbati wrote
> public/private keyword are unavoidable for classes because it's the only
> reasonable way to "protect" special functions like ctors, dtors, etc.
>
> You can't emulate private section through nesting as you can do with
> namespaces. You can write:
>
> namespace my_library
> {
> namespace implementation_details
> {
> /* implementation details */
> }
>
> /* public interface */
> }
>
> and easily obtain a poor-man's private block in a namespace. But the code:
>
> class my_class
> {
> private:
> /* implementation details */
>
> public:
> /* public interface */
> };
>
> is completely different from
>
> class my_class
> {
> class implementation_details
> {
> /* implementation details */
> };
>
> /* public interface */
> };
>
> The latter cannot replace the former. Sure, you could prefix each
> private name with "implementation_detail_" but things would get really
> ugly and you could no longer make a member "less private" in a derived
> class without changing its name.
What about
class my_class
{
class
{
/* implementation details */
} implementation_details;
/* public interface */
};
Should work.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: rmaddox@isicns.com (Randy Maddox)
Date: Sat, 18 Sep 2004 00:02:13 GMT Raw View
AlbertoBarbati@libero.it (Alberto Barbati) wrote in message news:<TPn1d.1380$2T6.35615@twister1.libero.it>...
> Radu Teodorescu wrote:
> >
> > Private namespaces
> > ------------------
> >
> > [snips]
> >
[snip]
> >
> > One idea that is to have public and private keywords for namespaces as
> > we have for classes (it would be very interesting to introduce
> > "namespace inheritance" and to use protected clause too, but this
> > would be the subject of a different paper). This will lead to very
> > good results. Let's show in an example a possible way of using this:
> >
>
> A typical idiom to solve this kind of issues is to use a nested
> namespace with a self-descriptive or deliberately ugly name (for example
> the Boost library uses namespace "detail"). Library users are warned
> that referring to any symbol declared in the detail namespace is looking
> for trouble. This measure is good enough to avoid any unintended use of
> such symbols. Maybe it may seem not enough for you, but adding a private
> keyword provides no better security: if I want to shoot myself on my
> foot, I can always edit the header file and comment out the line with
> the private keyword!
>
> IMHO, adding public and private keyword at namespace scope does not add
> any extra value.
>
[snip]
You can already differentiate between private and public within a
namespace by simply nesting an unnamed namespace inside a named
namespace, e.g.,
namespace X
{
namespace // unnamed => private to namespace X
{
// put whatever private stuff you want here
}
// put public stuff for namespace X here
}
Now everything inside the nested unnamed namespace is private to
namespace X.
No need for any change to the language. It's already in there.
Randy.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: AlbertoBarbati@libero.it (Alberto Barbati)
Date: Sat, 18 Sep 2004 04:57:29 GMT Raw View
Randy Maddox wrote:
>
> Now everything inside the nested unnamed namespace is private to
> namespace X.
>
That would work inside a cpp file, but here we are talking about
namespaces in a header file, so your remark is irrelevant.
Putting an unnamed namespace in a header file not only does not protect
its contents at all, but has the unwanted effect of duplicating its
contents in every translation unit that includes the file. This might
create a lot of problems, for example:
---MyLib.h
namespace MyLib
{
namespace
{
class should_be_private { ... };
void lib_implementation();
}
inline void lib_interface() { lib_implementation(); }
}
---MyLib.cpp
namespace MyLib
{
namespace
{
void lib_implementation() { ... }
}
}
---Main.cpp
#include "MyLib.h"
int main()
{
MyLib::should_be_private x; // No error here.
// But shouldn't it be private?
MyLib::lib_interface(); // Compiles but doesn't link. Doh!
return 0;
}
This produces on VC71:
error LNK2019: unresolved external symbol "void __cdecl
MyLib::`anonymous namespace'::lib_implementation(void)"
That's because you now have two copies of lib_interface(), one in
Main.cpp and one in MyLib.h, referring to two different declarations of
lib_implementation(). This, in fact, is a case of ODR violation.
Alberto
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: RTeodorescu@gmail.com (Radu Teodorescu)
Date: Mon, 13 Sep 2004 18:23:37 GMT Raw View
Hello all...
I forwarded the following proposal to the C++ standardization committee. I
think it's best that we do here a discussion about the described issues....
Here it is:
Proposals for a better C++
**************************
Definition of encapsulation
---------------------------
As in any object oriented programming language, one of the most
important things in C++ is (or should be) the encapsulation.
Encapsulation doesn't just mean an hierarchical organization of
things, but also hiding some of those things for outside use. For
example, classes are visible only in the class in which they are
defined, if they are defined using private or protected directives.
This means that those classes are hidden for the outside scope.
Let's take an example:
class Car
{
private:
class Engine
{
// ...
}
Engine theEngine;
public:
void DoSomethingWithTheEngine(...);
// ...
}
In this example we can see two things:
1. Engine depends on Car (in the real world, an engine is useless
without a car). This will lead us to the concept of hierarchical
definitions of the classes (we are not talking about the class
inheritance).
2. Engine is hidden in Car, so nothing can access the Engine class
outside the Car class (in the real world the driver doesn't have
direct access to the engine; only the car interacts with the engine).
This will lead us to the concept of hidden class definitions. If the
Engine class is made public, the second point has no sense, but the
first one is still valid. These concepts can be extended if we also
take in account the protected directive, but this is not the purpose
of this paper.
If we generalize this two points, we can define encapsulation as
"hierarchical organization" + "hiding code". The benefits of the
encapsulation are well known to every programmer: both "hierarchical
organization" and "hiding code" concepts lead to a code that is clear,
more understandabley and much more flexible.
Private namespaces
------------------
To avoid name duplicates, C++ introduced the concept of namespaces.
This concept can do much more than this, because C++ allows
hierarchical definitions of namespaces, making namespaces a powerful
tool for programmers.
Opposite to classes, namespaces are compile-time abstractions, and
their use doesn't affect the final executable file. This means that
there is no need for the whole namespace to be defined in only one
place. Indeed, C++ allows a namespace to be defined in several places.
The following example demonstrates this:
namespace MyLib
{
namespace Test
{
void f1();
// ...
};
void otherFunction();
// ...
namespace Test
{
void f2();
// ...
};
};
In this example both f1 and f2 functions are defined in the same
namespace (Test). This property (let's call it "namespace
distribution") of namespaces is very important, because you can split
the namespace across more files, each file containing the
declaration/definition of some items.
If the the "hierarchical organization" concept can be applied to
namespaces, the other concept of "hiding code" cannot be applied to
namespaces. For example for the above code, we cannot let the
programmer use the f1 function inside otherFunction, but prevent to
use it from outside of the MyLib scope. Most of the time, programmers
will rename the namespace of f1 to Private and make comments that no
one should use the things inside the Private namespace.
Sometimes, when the security is crucial, functions like f1 are hidden
in some helper classes. The fact that namespaces doesn't supply the
"hiding code" facility is certainly an inconvenience of C++ . Let's
take a better example for this issue. Let's assume that we want to
make an array of types, a concept similar to typelists. Our
"typearray" will be a template class that will receive the types as
template parameters (let's say that it can hold a maximum of three
types in the array - the size of the array doesn't really matter
here). Here is the base code:
namespace TA
{
struct NullType {}; // Represents "no type here" idiom
// The type array definition
template<typename T1=NullType, typename T2=NullType, typename T3=NullType>
struct TypeArray
{
enum { isEmpty = false };
};
template<>
struct TypeArray<NullType, NullType, NullType>
{
enum { isEmpty = true };
};
// Used to get the the type at the specified postion
template <typename TA, int idx>
struct TypeAt { typedef NullType Result; };
template<typename T1, typename T2, typename T3>
struct TypeAt<TypeArray<T1,T2,T3>, 0> { typedef T1 Result; };
template<typename T1, typename T2, typename T3>
struct TypeAt<TypeArray<T1,T2,T3>, 1> { typedef T2 Result; };
template<typename T1, typename T2, typename T3>
struct TypeAt<TypeArray<T1,T2,T3>, 2> { typedef T3 Result; };
// Used to get the size of the array
template <typename TA>
struct Size { enum { value = 0 }; };
template<typename T1, typename T2, typename T3>
struct Size<TypeArray<T1,T2,T3> >
{ enum { value = 1 + Size< TypeArray<T2,T3,NullType> >::value }; };
template<typename T2, typename T3>
struct Size<TypeArray<NullType,T2,T3> > { enum { value = 0 }; };
};
The code is not very complicated: the TypeArray template class holds
the type information in the template parameters; the TypeAt template
class returns the type at the specified index in the specified
typearray in its internal type Result; the Size template class returns
in value the size of the typearray given as a parameter. All the
templates are using partial specialization, so they can be defined
only at namespace scope.
The TypeArray template class receives three parameters (again, it
could receive much more parameters, but we are trying to get things
simpler). If the user wants to have a type array that contains two,
one or zero elements, he can simply ignore the rest of the parameters,
as the template takes their implicit values: NullType. This type is
very special, as it marks the end of the array. It's just like the
null terminator for a C-style string. The Size class template returns
the index of the first NullType in the typearray.
What happens if the user creates an typearray with the code "typedef
TA::TypeArray<int, NullType, char> MyTypeArray"? The best approach for
the designer of the typearrays will be to state "this thing shouldn't
happen". But unfortunately, according to the current standard, there
is no way of stopping such things to happen (we incapsulate NullType
and TypeArray in a class because TypeArray is specialized, and
specializations are allowed only at namespace scope). This shows that
the current standard of C++ lacks here, and leads us to the conclusion
that the new C++ standard must implement a method of declaring private
namespaces.
One idea that is to have public and private keywords for namespaces as
we have for classes (it would be very interesting to introduce
"namespace inheritance" and to use protected clause too, but this
would be the subject of a different paper). This will lead to very
good results. Let's show in an example a possible way of using this:
namespace MyLib
{
private:
void f1();
// ...
public:
void otherFunction();
// ...
};
void outsideFunction();
// ...
namespace MyLib
{
void f2();
// ...
};
In this example, otherFunction can call f1, but outsideFunction can't
call f1. But should f2 be allowed to call f1? This question can be put
in this form: "Should it be allowed to share private declarations over
different namespace distributions?". At first impulse, we might say
that the answer should be "yes". If the answer is "no" we will
introduce restrictions for distributing the implementation of a
namespace into multiple files, because we will not be able to share
private declarations between different files. But if we say "yes", an
outsider can access these private declarations by extending the
namespace in which the declarations are.
The undeclare and replace_declaration keywords
----------------------------------------------
By using the extern keyword, we can say to the compiler "this
declaration exists somewhere else, we can use it", but there is no way
that you can tell the compiler "this declaration exists, but don't let
anyone see it". It will be a good idea to have in the new C++ standard
an undeclare keyword that will un-declare a certain declaration. In
our typearray example we can write an "undeclare class NullType;"
directive at the end of the TA namespace, to solve our problem.
Let's have another example: one has found a better method of
implementing strcpy, and he wants every source file that includes a
certain header to use his function instead of using the default
function. In this case he probably wants to write in his header:
undeclare ( char* strcpy(char*, const char*) );
char* strcpy(char*, const char*);
Of course, he will have to implement in a source file the new routine.
When he does this, a linker error will occur: what implementation to
use for function strcpy? Yes, this approach is bad, and shouldn't be
allowed, because with the second line of code will declare the strcpy
function again, and the compiler should not take in account that (we
want to avoid redeclaring things when we use undeclare).
To give a possibility to the programmer, other than using the #define
directive to substitute the texts, we should have in the new standard
a replace_declaration keyword that will do a "safe" substitution of
declarations. The header from the previous example should look as
following:
char* my_fast_strcpy(char*, const char*); // fast version of strcpy
undeclare ( char* strcpy(char*, const char*) );
replace_declaration ( char* strcpy(char*, const char*), my_fast_strcpy );
This way, each time strcpy is called, the compiler redirects the call
to my_fast_strcpy as strcpy is now an alias to the fast string-copying
function. Of course, my_fast_strcpy must be declared in the same way
as strcpy.
To summarize, we should have the following rules for these two new keywords:
- if we use replace_declaration(decl, other_decl), decl and other_decl
must be of the same type
- if we use replace_declaration(decl, other_decl), whenever decl is
issued the compiler uses other_decl
- after an undeclare keyword, the undeclared declaration shouldn't be
allowed to be used any more
- if we use replace_declaration(decl, other_decl) after
undeclare(decl), the decl can be issued afterwards, because other_decl
will be used instead.
Template specialization at class scope
--------------------------------------
Currently in C++ one cannot specialize a template at the class scope,
although it can define the template (some compilers - like Visual C++
6.0 - supports this feature). This will break both of the two rules of
encapsulation we defined above.
If we consider again the typearray example, we might want to introduce
the TypeAt template class into the definition of TypeArray, so we can
easily access the elements in an array of types. A better example for
this cause will be with the Size class template. Let's say we
implement and use about ten algorithms over the type arrays, and for
each one we need the size of the array that they are operating with.
We would have to invoke the Size template in each case to get the size
of the array, and every time the same operations will be performed to
compute the size of the type array. This will increase the compiling
time. The best solution would be to keep the size of the array within
the array template class, and of course, to define the Size template
inside the TypeArray class (we cannot put the Size definition in front
of the definition of TypeArray because Size uses TypeArray). As the
Size template class, the way we have defined it, is based on template
specialization, according to the current C++ standard is not valid to
put it inside the TypeArray class. If we put the Size template inside
the TypeArray class, we would make it private, and we shouldn't think
of specializing this outside the TypeArray class.
The best solution would be to introduce in the standard the
possibility to define and specialize templates inside classes (normal
classes and class templates).
Typedef templates
-----------------
Another lack in the current C++ standard is the fact that it's not
allowed to have templates of typdefs. Alternative solutions can be
used, like we used for TypeAt template class. Most of these solutions
will use (like we did) a template of class that contains an inner
typedef. These solutions would guarantee a hundred percent
compatibility with a typedef template. Still, programmers who will use
these workarounds must be careful to always use the inner type and not
the class template. Let's see how TypeAt should look if templates of
typedefs were accepted by the standard:
template <typename TA, int idx>
typedef NullType TypeAt;
template<typename T1, typename T2, typename T3>
typedef T1 TypeAt<TypeArray<T1,T2,T3>, 0>;
template<typename T1, typename T2, typename T3>
typedef T2 TypeAt<TypeArray<T1,T2,T3>, 1>;
template<typename T1, typename T2, typename T3>
typedef T3 TypeAt<TypeArray<T1,T2,T3>, 2>;
In this example we have also shown how specialization of typedef
templates can be used.
Radu Teodorescu
09/09/2004
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: AlbertoBarbati@libero.it (Alberto Barbati)
Date: Tue, 14 Sep 2004 03:18:03 GMT Raw View
Radu Teodorescu wrote:
>
> Private namespaces
> ------------------
>
> [snips]
>
> What happens if the user creates an typearray with the code "typedef
> TA::TypeArray<int, NullType, char> MyTypeArray"? The best approach for
> the designer of the typearrays will be to state "this thing shouldn't
> happen". But unfortunately, according to the current standard, there
> is no way of stopping such things to happen (we incapsulate NullType
> and TypeArray in a class because TypeArray is specialized, and
> specializations are allowed only at namespace scope). This shows that
> the current standard of C++ lacks here, and leads us to the conclusion
> that the new C++ standard must implement a method of declaring private
> namespaces.
>
> One idea that is to have public and private keywords for namespaces as
> we have for classes (it would be very interesting to introduce
> "namespace inheritance" and to use protected clause too, but this
> would be the subject of a different paper). This will lead to very
> good results. Let's show in an example a possible way of using this:
>
A typical idiom to solve this kind of issues is to use a nested
namespace with a self-descriptive or deliberately ugly name (for example
the Boost library uses namespace "detail"). Library users are warned
that referring to any symbol declared in the detail namespace is looking
for trouble. This measure is good enough to avoid any unintended use of
such symbols. Maybe it may seem not enough for you, but adding a private
keyword provides no better security: if I want to shoot myself on my
foot, I can always edit the header file and comment out the line with
the private keyword!
IMHO, adding public and private keyword at namespace scope does not add
any extra value.
>
> The undeclare and replace_declaration keywords
> ----------------------------------------------
>
> By using the extern keyword, we can say to the compiler "this
> declaration exists somewhere else, we can use it", but there is no way
> that you can tell the compiler "this declaration exists, but don't let
> anyone see it". It will be a good idea to have in the new C++ standard
> an undeclare keyword that will un-declare a certain declaration. In
> our typearray example we can write an "undeclare class NullType;"
> directive at the end of the TA namespace, to solve our problem.
"undeclare" might have some use, but frankly I don't feel the urge to
have such a feature. I would hate the proliferation of undeclarations.
Anyway, until we have the dreaded preprocessor, one can easily undefine
a symbol by replacing it with a macro.
However, the hack you describe in the following to redeclare symbols is
even worse than the use of preprocessing macros. I strongly object to
its usefulness and necessity.
> Let's have another example: one has found a better method of
> implementing strcpy, and he wants every source file that includes a
> certain header to use his function instead of using the default
> function. In this case he probably wants to write in his header:
>
> undeclare ( char* strcpy(char*, const char*) );
> char* strcpy(char*, const char*);
Why would one undeclare something and then redeclare it in the same way?
If I want my program to use my version of strcpy (forgetting, for an
instant, that it's probably not a good idea), the only thing I need to
do is:
1) define strcpy (no need to declare it!!!)
2) inform the linker to use my version instead of the one contained in
the library
Where's the need to undeclare/redeclare? If I include <cstdlib>, strcpy
gets declared. So what? Does the compiler know to which function the
strcpy symbol will be bound to? No. It's the linker that performs the
binding. Everything can be arranged more easily by setting the right
flags on the linker command line. Why bother the compiler about a linker
detail?
But, more important: how many times in your programming life did you
encounter this use case in writing portable code? I can answer according
to my limited experience: zero.
>
> Template specialization at class scope
> --------------------------------------
>
> Currently in C++ one cannot specialize a template at the class scope,
> although it can define the template (some compilers - like Visual C++
> 6.0 - supports this feature). This will break both of the two rules of
> encapsulation we defined above.
>
??? You *can* specialize a template at class scope!! What version of C++
are you referring to? Sure, if you take VC++ 6.0 as an example of a good
C++ implementation of template support you are completely out of target.
>
> Typedef templates
> -----------------
>
You clearly have never followed the discussions on this newsgroup
before. This issue has been discussed for a very long time. You can have
a look at the currently more advanced proposal here:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1449.pdf
Regards,
Alberto
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]