![](/i/fill.gif) |
![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
nemesis <nam### [at] gmail com> wrote:
> > So in your opinion the concept of "interface" is a verbal agreement about
> > what should and shouldn't be used from a module? *That* is what sounds like
> > bureaucracy to me.
> Not bureaucracy at all. Just a list of names of functions a module
> provides.
I really fail to see how that is different from using a *real* interface
specification, rather than just a verbal one. Except for the fact that with
a real interface the compiler can make sanity checks, of course.
> >> For people who know it better, the lack of such measure does not mean
> >> irrestrict access to things, it simply means less stupid typing to make
> >> the compiler happy. Modularity is conserved.
> >
> > In Utopia, maybe. In the read world, not so.
> Indeed. In real enterprise world we got people who don't enjoy nor know
> how to program, programming.
Yes, and these people put everything in the public part of their classes
because they don't have any idea what they are doing. In the same way as
those self-taught hackers who detest all programming paradigms.
> > And I don't consider having to write "private:" once to be significantly
> > more typing.
> Indeed, Delphi and C++ are yet more zippy at that, not so much for those
> poor Java souls... you also forgot the data type declarations.
In fact, in C++ you don't even have to necessarily write "private:" at
all because members are by default private (when not inside any access
specifier block).
> > If I had to document what you should and should not access
> > in the module, *that* would be significantly more typing.
> This coming from a guy whose favorite language needs for one to provide
> the source and the header file.
What source?
> > Also, if I make a module for others to use, how can I guarantee that
> > I can change the implementation later without breaking code others have
> > written?
> Don't change the interface. Of course, if the implementation is changed
> in such a buggy way as to totally alter the behaviour, same interface
> doesn't mean jack.
Don't write such BS. I am talking about experience. For example I have
totally changed the internal implementation of this class of mine several
times, and the public interface has always remained the same regardless,
and thus the changes have never broken any existing code:
http://warp.povusers.org/FunctionParser/
And by "totally" I mean literally write everything from scratch. And
no external code breaks. That's the nice thing about modularity. It allows
me to do that safely.
> >> It's all about scope. If it's public, it should be used. If it's not,
> >> you shouldn't.
> >
> > And what's wrong with the compiler telling you if you try to use it
> > regardless? I really can't understand that.
> Nothing wrong except quite a few more tiresome and bureaucratic measures
> before actually writing anything.
Thinking that brevity in programming is a good thing is a really common
beginner mistake. Those are the beginners who, for example, in C++ always
write "using namespace std;" to dump *everything* in the standard libraries
to global namespace because it saves them from typing 5 extra characters
later. Then you get code like this:
i = search(first1, last1, first2, last2);
"Woohoo! I saved typing 5 characters!" And now it's completely unclear
what this "search()" is. Is it a local function somewhere? Is it an external
function defined in some header? Perhaps it's a class which is being
instantiated? Is it a standard library function?
Compare to:
i = std::search(first1, last1, first2, last2);
Now it's completely unambiguous that this is a standard library function
being called.
Brevity is *not* always the answer. Even if I had to write "private" in
front of every single function and variable declaration (as you have to do
in some languages), I would still do it. (Fortunately I don't have to.)
> > The compiler is not limiting anything that you aren't *already* limiting
> > by your verbal agreement. The only thing the compiler is doing is checking
> > that the agreement is not broken. Why that is a bad thing is beyond my
> > comprehension.
> I was not saying that is bad. I was just reacting to this: "Your
> definition of object-oriented programming may vary, but in my view
> data hiding, ie. modularity, is an integral part of OO programming."
> Modularity is still there regardless of dumb programmers ripping their
> contracts and no middleman to prevent them doing so.
I disagree. There's no modularity if you can't enforce access rights.
Data hiding is an integral part of modular design. Verbal agreements are
worth absolutely nothing.
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Darren New <dne### [at] san rr com> wrote:
> Warp wrote:
> > 1) I write "private:" once in my class definition.
> >
> > 2) I write extensive documentation about the subject and hope people will
> > read it and obey it. Many people won't.
> You missed this one:
> 3) You name your private variables with the naming convention used by
> every other package ever implemented in that language, including
> the ones that define the runtime, and which every programmer knows
> bypassing means future incompatibility.
I still prefer option #1. It's not like it would exclude option #3, so
you get the best of both worlds: Clarity in naming conventions, *and* the
compiler checking the integrity of the interface.
> > And what's wrong with the compiler telling you if you try to use it
> > regardless? I really can't understand that.
> You already know you're using a private variable, if you're bypassing either
> global naming conventions or using reflection or something.
> If the compiler actually *prevents* it, then you're locking yourself out of
> a whole bunch of metaprogramming techniques that templates aren't adequate
> to handle.
And if you don't prevent it, you are locking yourself out of ever trying
to improve your module (without breaking tons of existing code).
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
nemesis <nam### [at] gmail com> wrote:
> Rule #5: Best documentation is the code. ;)
In fact, precisely. If the code supports telling "this is the public
interface", what better documentation.
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
nemesis <nam### [at] gmail com> wrote:
> Warp escreveu:
> > And again, you are hacking on
> > purpose, trying to bypass the compiler checks, rather than using those
> > checks to catch your mistakes.
> How this is any different from someone breaking a contract on purpose is
> beyond me.
It's different because if you do it by mistake, nothing will tell you.
(Except when your code breaks when you update the library.)
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
> I really fail to see how that is different from using a *real* interface
> specification, rather than just a verbal one.
It's not "verbal". It's expressed in the code. It just so happens that
there's a way to bypass it. You say "My module exports the functions alpha,
beta, and gamma." You then later define alpha, beta, gamma, and delta. The
caller can't get to delta without explicitly saying "hey, you know that
private function delta in that module? Import it into my module."
It's not something you do by mistake.
> http://warp.povusers.org/FunctionParser/
>
> And by "totally" I mean literally write everything from scratch. And
> no external code breaks. That's the nice thing about modularity. It allows
> me to do that safely.
And then someone comes and says "Gee, parsing this function each time is
adding too much overhead. We need to store that structure in a database. And
then we need to calculate it at the server and then ship the bytecodes to
each client." And you're either screwed, or you bypass the private part to
make it work knowing you'll break on the next release of the library. Or you
pick the source up, and maintain it yourself in the future, which is just a
special case of #2 there.
> Brevity is *not* always the answer.
You should try Ada! ;-)
In any case, that sort of import is exactly the sort of way you bypass the
access restrictions on exported functions in Python, for example. You have
to go read the source (to find the name), then explicitly copy the function
into your own namespace to invoke it.
> I disagree. There's no modularity if you can't enforce access rights.
And you can't enforce access rights in C++, because nothing prevents wild
pointers from clobbering anything in memory.
WarpsClass x(...);
memset(&x,0,23);
If you think that's somehow unfair, then how is reflection any less fair?
--
Darren New, San Diego CA, USA (PST)
My fortune cookie said, "You will soon be
unable to read this, even at arm's length."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Darren New <dne### [at] san rr com> wrote:
> > (Well, at least not without some serious hacking.)
> Running off the end of an array is "serious hacking"? I wouldn't think so.
Now I'm completely puzzled. We are talking about public interfaces and
private members, and you start suddenly talking running off the end of an
array? I can't see *any* connection whatsoever between these two things.
I could just invent a completely wild answer to that argument of yours as
well: "But loops use loop variables without the need of any hacking." It
makes exactly as much sense to me.
> > I don't understand what you are saying. If a class in C++ does not expose
> > a member array, then you can't get to that member array from the outside,
> int * p = (int *) 0x123456;
> *p = 27;
> Find the bug in your code that's setting that member variable to 27.
What member variable?
> How about
> // Your code...
> class xyz {
> public: int[10] x;
> private int y = 0;
> }
> // My code
> xyz pdq;
> pdq.x[10] = 27;
You are accessing a public variable, not a private one.
> > If you need to tell someone else "don't access member variables whose
> > name start with an underscore", that's just plain stupid. You should be
> > able to tell that by making the *compiler* enforce it.
> I agree, except for the fact that you're using C++ as an example. :) C++ has
> it's own different lack-of-modularity problems. If the compiler actually
> *enforces* that the member variables you declare private can only be changed
> by your own methods, that's great.
It enforces it as long as you don't deliberately try to bypass the
compiler checks.
Deliberately hacking yourself around the compiler checks requires more
malice than using a public member variable which simply "shouldn't" access.
It sounds like you are saying "since you can't enforce the privacy of
the members in all possible cases, then they might just as well be public".
That's BS.
> In other words, if they really need to violate the privacy of
> your class, they're going to do it in C++ almost as easily as they do it in
> something like Python or LISP.
Haha.
> The compiler doesn't enforce the
> encapsulation in C++ - it just gives warnings when you use a variable name
> that's private.
Warnings? Trying to access a private variable gives a compiler error.
> Granted, if you don't *document* in a universal kind of way what's public
> and what isn't, you're leaving yourself open to abuse.
That's the great thing about language support for private sections: You
don't *need* to document that they are private. The compiler will tell you.
> There are also a whole bunch of assumptions in your classes that someone is
> going to rely on that you're not going to be able to prevent. SimCity
> assumed memory you called free() on would still be valid until the next
> malloc(), which is obviously a bad assumption yet still has to be supported
> by the infrastructure if you expect to run simcity on later versions of the
> OS.
I don't see any connection between that and the current subject.
> Or, in short, you can't protect against bad programmers
So let's deliberately make bad code?
That's exactly as silly as saying that it's useless to lock your car
because someone could break its windows anyways.
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Darren New <dne### [at] san rr com> wrote:
> > http://warp.povusers.org/FunctionParser/
> >
> > And by "totally" I mean literally write everything from scratch. And
> > no external code breaks. That's the nice thing about modularity. It allows
> > me to do that safely.
> And then someone comes and says "Gee, parsing this function each time is
> adding too much overhead. We need to store that structure in a database. And
> then we need to calculate it at the server and then ship the bytecodes to
> each client."
That's a hilariously bad example because in order to get the object from
the database you will have to make a database call, which will parse its
internal structures (perhaps even directly from disk), after which your
object will be constructed from this parsed data. I would be really surprised
if that's not an order of magnitude slower and resource-heavy than re-parsing
the function.
> And you're either screwed, or you bypass the private part to
> make it work knowing you'll break on the next release of the library.
And exactly how would making the private members public help this?
> > I disagree. There's no modularity if you can't enforce access rights.
> And you can't enforce access rights in C++, because nothing prevents wild
> pointers from clobbering anything in memory.
You are nitpicking, and you know it.
If you write normal, standard-conforming C++, you can perfectly well
enforce access rights.
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Darren New <dne### [at] san rr com> wrote:
> Sure. "Don't read documentation" or "forget documentation" is as bad as
> "don't document it". But if it's a naming convention used throughout the
> language, people don't do it.
And if it's a language feature, people don't do it either, not even by
accident.
> > Why is it a bad thing to make the compiler check interface breaches is
> > beyond my comprehension.
> First, the whole "dynamic languages" bit at the start should indicate not
> everyone has a compiler for these languages. Secondly, it eliminates a whole
> raft of useful mechanisms. Where's the C++ library to which I pass a class
> and it generates a SOAP interface parser for that class?
And then you modify your class, and your pre-existing SOAP interface parser
breaks.
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Darren New <dne### [at] san rr com> wrote:
> >> Or just using compiler-specific hackage to access the variables.
> >
> > That has been made rather difficult in practice.
> Err, has it? I can't say something like
> int * p = (int*) ((void*)(&yourInstance) + 32);
> if I want access to whatever integer is 32 past the start of your class?
You call that "easy"? To me it looks like garbage. You can't write code
like that by mistake. You are deliberately writing non-standard code which
triggers undefined behavior. You are not accessing a member variable, you
are trashing memory.
Let's talk about *standard-conforming* C++.
> > If you are *intentionally*
> > trying to break your program, then that's your choice, of course. However,
> > in normal usage it's useful if the compiler checks that you don't make
> > mistakes.
> While I agree, I fail to see why using reflection as intended isn't "normal
> usage" and not "a mistake". It's not something you accidentally do. You know
> very well you're bypassing encapsulation. Not the least of which is that you
> need to give the name of the variable you want to access as a string.
Well, if you want to intentionally break the modularity of your code with
something like reflection, at the risk of breaking tons of code in the future,
that's your choice.
(If you use reflection to, for example, save the state of an object to
a database to be retrieved later, haven't you kind of locked up the
structure of that object? You can't make *any* changes whatsoever to it
without breaking all the stored objects in the database. Doesn't sound
very enhanceable to me.)
> > Whether your program will link after that can depend on the compiler.
> > It's certainly not standard-conforming. And again, you are hacking on
> > purpose, trying to bypass the compiler checks, rather than using those
> > checks to catch your mistakes.
> Right. Since you said reflection violates encapsulation hard enough to make
> a program not "OO", I'm trying to figure out allowing type casts and
> assignment of arbitrary memory addresses to pointers doesn't.
If you trash memory, you are writing non-portable, non-standard-conforming
code. All bets are off. If you want to write code like that, you might as
well not use classes at all. What's the point?
> If the statement is "the compiler should error out on external access to
> private variables, but there should be a non-portable hacky and error-prone
> way to bypass it anyway, so you can't rely in your debugging efforts on what
> your code actually says," I'll just have to disagree with you there. :-)
Where have I said "there should be a non-portable way to bypass it"?
There may be a non-portable way of bypassing it. That's different from
"there should be".
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>> Warp wrote:
>>> 1) I write "private:" once in my class definition.
>>>
>>> 2) I write extensive documentation about the subject and hope people will
>>> read it and obey it. Many people won't.
>
>> You missed this one:
>> 3) You name your private variables with the naming convention used by
>> every other package ever implemented in that language, including
>> the ones that define the runtime, and which every programmer knows
>> bypassing means future incompatibility.
>
> I still prefer option #1. It's not like it would exclude option #3, so
> you get the best of both worlds: Clarity in naming conventions, *and* the
> compiler checking the integrity of the interface.
I disagree. Now you've got a naming convention that isn't enforced that
would mean something to people trying to read your code. Duplicate work,
with uglier source code, and insufficient reliability to depend on. I think
we've all learned that faux-Hungarian notation is a bad idea.
Unless the compiler also enforces that (say) privately scoped variables
start with an underline, at which point, why? What prefixes are you going to
have the compiler enforce, and what benefit does it bring, and why not just
have the prefix indicate the type information (like trailing $ means string
in basic)?
>> If the compiler actually *prevents* it, then you're locking yourself out of
>> a whole bunch of metaprogramming techniques that templates aren't adequate
>> to handle.
>
> And if you don't prevent it, you are locking yourself out of ever trying
> to improve your module (without breaking tons of existing code).
Not really. The beauty of the reflection libraries are that they allow the
person using them to *adjust* what they do when you change your code, all
without having to change *their* code. Does using
write(handle, &thestructure, sizeof(thestructure))
prevent you from ever adding anything to thestructure? No. Why? Because
you're getting metadata from the compiler (via sizeof) rather than
hardcoding a 137 as the size or some such.
Of course, that's hardcoded at compile-time, and the results it writes out
can't be read into an instance of the new version of the structure, which is
exactly why people started building more complex reflection libraries,
hibernation libraries, and so on.
If you have something like your function parse library, and I want to take
an instance of that and send it to a process on a different machine, how do
I do that? I can't, without violating the encapsulation, because you didn't
provide for that. But if my income this week depends on me getting to your
private variables, C++ isn't going to be able to stop me.
(I may be wrong, but it doesn't look like you provide accessors for anything
in the instance, so I can't even look at it after the fact and see what
function it's representing? It looks like I can't have a function that takes
an instance of FunctionParser and pulls out enough information that I can
make a second FunctionParser that does the same thing, other than via
AddFunction (second overload)?)
But with reflection, I can write code that does this, *and* it will *still
work* even after you change your class, even if you rewrite it completely.
Hacking past "private" is a whole different ball of wax than reflection.
Hacking around "private" can be a mistake (bad pointer) or intentional
(#define private public), but reflection is always intentional *and* it's
easy to write it to self-adjust.
--
Darren New, San Diego CA, USA (PST)
My fortune cookie said, "You will soon be
unable to read this, even at arm's length."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |