[...]
i love C++. i think its one of the best things ever.
but i happen to
agree with Erik. the solution proposed by martijn doesn't scale well,
and doesn't really address the issue in a comprehensive way. it
requires one pure virtual class per distinct set of private members,
for a start.
No, it requires a pure virtual class per distinct interface (abstract
class). And I don't see why this would not scale.
the kinds of problems i have with C++ stem from the
fact that you
cannot mark a section of protected members as accessible from only a
particular set of friends. the only way to say "only class Foo can
access these member (functions)" is to create a separate class, make
Foo a friend of that class, and then inherit from it.
This really is a different problem.
A friend is just like a member function, i.e. it can access the class'
private
data, but it is not in the class' scope and the function is not invoked on
an object. Declaring a class A friend of another class B is saying that all
member functions of A have access to the private part of class B. This
should only be done to exporess closely connected concepts.
When possible other classes should interact using a class' public
interface(s).
this gets really messy really soon. the editor object
in Ardour
contains many distinct sets of functionality that i would really like
to partition.
So, derive from various abstract classes that provide interfaces
for these sets of functionality.
i could create a set of discrete classes that cover
each
aspect of the functionality, and then do MI from all of them. the
problem is that is each one of these aspects *internally* needs to
know about the others, which now means that they each have to be
friends of each other. so what's the point? i'd end up with something
ludicrous like:
class EditorFoo {
...
protected:
friend class EditorThis;
friend class EditorThat;
friend class EditorTheOther;
...
friend class ObjectThatInteractsWithEditorFoo;
...
};
Here EditorFoo is not a abstract class.
...
class Editor : public EditorFoo, EditorThis, EditorThat,
EditorTheOther .... {
}
which is really no help at all.
class A { // some interface
public:
virtual void a() = 0;
};
class B { // another interface
public:
virtual void b() = 0;
};
class editor : public A, public B { // the editor provides both interfaces
public:
void a();
void b();
};
and now a class C that wants to use the functionality of the editor that is
exported by interface A can use it like:
void f(A& i) {
i.a();
}
without depending on the implementation.
Note that your problem is still different as with using friends in such a
manner the classes using the editor are dependant on its implementation
and also don't use the editor's public part only.
the other alternative is to use HAS-A
instead of IS-A, and its even worse. we end up with lots of code like:
editor->foo->...
editor->that->...
editor->theother->...
This is an option.
all i'd really like it to be able to say:
class Foo {
protected:
/* scope A */
friend class Bar;
...
protected:
/* scope B */
friend class Baz;
...
};
such that Bar can only access stuff within "scope A" and Baz can only
access stuff in "scope B". that is, access control keywords
("private", "protected", "public" define access scopes).
right now, a
friend class declared anywhere within the class declaration is a
friend, period.
Correct. But perhaps you are misusing the friend concept. Are these
friend classes so closely related that they cannot use some public
interface?
none of this helps with the problem erik identified.
The problem Erik identified was that one could not seperate the
interface from the implementation in C++. I then said this can be
done using an abstract class, i.e. an interface.
i would really
love a C++ that did this:
class Foo {
public:
... stuff ...
private:
<some tokens that meant something like #include>
};
and then in other files, you would do:
#use "foo.h"
or
#implementation "foo.h"
or something like that. the first one would just bring in the public
declarations, and its what you'd use when you're calling or creating
Foo objects. the second one would be used in the definitions for the
member functions of Foo, and hence would almost certainly be limited
to "foo.cc".
It doesn't work that way. You cannot create an class instance without
its full declaration. I don't see the problem of having the private part
of a class in the header. If you want to be seperated from the
implementation, use an abstract class. But then you cannot create
a class instance, you can only use the provided interface on an already
created instance (, but you can delete it when there is a virtual destructor
in the interface).
note that the private stuff would be in a file that
looked just like
the class declaration, but had no public (and protected?)
declarations. in other words, the class declaration it includes is
implicitly 100% private, but it can include header files that are
necessary for the private declarations.
I don't see what this would solve and I don't think this is even possible.
Changing the private part will break binary compatibility (without
changing the public header).
--ms