On Wednesday, October 30, 2013 12:12:10 AM UTC-4, Billy O'Neal wrote:>> Unit tests that test non-public data are bad (brittle) unit tests. Break up those classes! :)99% of the time I agree, however there are some cases where it isunavoidable. One example is optimizations. For example, perhaps yourclass is a template that is optimized for POD types. The effect maynot actually be visible from the public interface, in which case youmay need to dig into the private interface to ensure the optimizationtriggers for all of the right cases and works as expected.Right now this is impossible to do without doing horrible things likeadding extra public methods, friend declarations, or making everythingprotected and inheriting. All of these are horrible.On Wednesday, October 30, 2013 11:02:01 AM UTC-4, Ville Voutilainen wrote:>>> I have also used a solution where I define a public struct for the members of the class> and have the members in a private instance of that struct, and then pass a reference> to that struct to implementation-file-scope 'private' functions. This way the only> private member functions that need to be in the class definition are the ones that> are virtual.This works but I'm not sure I like it as it exposes another public symbol.The other trick is the private nested class with static methods. Nosymbols are exposed to the user but this is still a coupling of theclass definition with the private member implementations.Whoever implements the private methods, regardless of how many C filesare used is restricted to using this one nested class.On Sunday, November 3, 2013 8:44:45 PM UTC-5, Philipp Stephani wrote:>> Adding a virtual member functions to a class without other virtual member functions does change the class layout. (I guess classes whose virtual member functions are all private don't occur in practice though.)Actually all virtual functions with the exception of the destructorshould almost always be private. This is the non-virtual idiom.http://www.gotw.ca/publications/mill18.htm On Sunday, November 3, 2013 12:10:17 PM UTC-5,
mitc...@gmail.com wrote:
>
> I've been thinking about a new proposal for a "class implementation namespace",
This is pretty interesting. One potential abuse is that if anyone
wants to just avoid your public interface they can open the namespace
and hack in something. I could see horrible last minute bug fixes
being implemented this way. Still, access control is just a tool for
allowing us to write better interfaces, if people want to do stupid
things we don't need to be responsible for them.
The friend idea I proposed limits the access control to the
implementation of member functions. Only someone writing the actual
member functions can extend the access control out to another
function.
On Sunday, November 3, 2013 9:06:39 PM UTC-5, Philipp Stephani wrote:
>
> I'm not sure whether a restriction should be made to only allow private nonvirtual functions outside the class definition -- a rule could be added that the class layout may not be different for any nonzero number of virtual functions, and the compiler could signal an error if a virtual function is to be added to a class whose definition doesn't contain any virtual functions.
As suggested by others, virtual functions must be in the class
definition. Every part of the interface which is exposed to users must
be in the class definition, it should not be spread out and makes
little sense to do so. More on that later.
On Monday, November 4, 2013 12:56:11 AM UTC-5, Thiago Macieira wrote:
>
> Well, the one thing I'd want is to have a real static (as in file-local)
> method, to control my exports.
>
> I know all the techniques. But I am forced to use compiler extensions in order
> to control the list of exports from my TUs and libraries, to degrading the
> linkers' performances due to way too many symbols.
I was under the impression that static and anonymous namespace
functions don't export symbols even in single object files. In fact
the function may be removed entirely or even sliced up into callable
pieces if the compiler decides to inline it everywehre.
With regards to the general problem of symbol visibility, that's
probably something that would probably involve [attributes] and is
outside of this discussion entirely.
On Monday, November 4, 2013 8:18:22 AM UTC-5, Olaf van der Spek wrote:
>
> Not entirely true. In general, callers don't need to know the layout. The layout is (only?) required when accessing data members, the size is only required when creating an object on the stack.
> Not requiring private data members in the header would be nice too IMO.
But how would you compute the size without knowing the layout? You
need to know the layout for padding. Also, what real use is knowing
the size without knowing the layout. As soon as you change a data
member your callers will need to recompile anyway as the size will
change.
On Monday, November 4, 2013 2:41:35 PM UTC-5, Thiago Macieira wrote:
>
> Maybe the solution will come with modules, when we can finally have a
> standardised syntax for "this method is called by other modules" versus "this
> method is never called externally"
Maybe, maybe not. What are modules? When will they become available?
We have no idea. I'd like a workable solution to this problem now.
I'd like to focus the discussion a bit. Lets agree on some invariants.
The following must remain in the class definition.
* data members with any access control.
* virtual methods with any access control.
* public and protected non-virtual methods, they are part of the interface.
* private non-virtual methods called by inline functions, for obvious reasons.
Maybe someone can come up with a good reason to break one of these but
I don't want to discuss it here. If you're passionate about that then
please start a new thread.
Not only are there hard implementation reasons for these, restricting
the external interface to a single point, namely the class interface
is an extremely powerful constraint in terms of usability and
readability. I don't need to chase down multiple header files, source
files, and documentation, I just need to see one class definition and
it has everything I need to know as a possible user of the class
(using directly or inheriting). The only thing I don't need to know
about is how many functions are used to implement its behavior, and
those are the non-virtual private methods we are discussing.
So far we have 2 suggestions, declaring friends in function bodies and
having a class namespace.
Here's another, simply define allow a syntax to define new private
methods outside of the class definition. These methods should *always*
be private and therefore only callable by other class methods. What we
don't want is to allow people to start writing public or protected
extension methods, making interfaces impossibly complicated. This
allows easy extensibility and also keeps the interface constrained to
whats in the definition.
class Foo {
public:
Foo(int i);
};
//explicitly define a new private method which is not in the class body
explicit void Foo::_foo() {
}
//explicitly define a new private constructor, callable by other constructors
explicit Foo::Foo() {
}
//Define the public constructor which calls the explicit private one
using delegation
explicit Foo::Foo(int i) : Foo() {
}
I like this better than the namespace proposal, because with the
namespace, you have this problem:
class Foo namespace {
//Hundreds of lines of code
//Wait, is this an extension method or a normal function? I have to look for the namespace tags.
void foo();
};
Also if the only thing that's going in your namespace is function
definitions, do you really need to create a scope for that? I always
prefer to use static for file local functions instead of anonymous
namespaces. I use anonymous namespaces for file local global variables
and class definitions.
The namespace idea however would allow you to define other things such as private nested types and typedefs.
I also like it better than the friend proposal, because adding friend
to already cluttered function bodies is hard to read and rather
intelligent. Its also not obvious looking at a function knowing
whether or not something else has declared it as a friend.