 |
 |
|
 |
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> 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.
Both Python and LISP and Java and C# provide for that. They just don't fail
to provide defined mechanisms for bypassing that.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> And then you modify your class, and your pre-existing SOAP interface parser
> breaks.
No, it doesn't. Because the reflection library will change what it sends,
and generate a new WSDL that the client will use to get the new interface.
That's why it's done that way.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> It's different because if you do it by mistake, nothing will tell you.
> (Except when your code breaks when you update the library.)
The things we're talking about generally don't allow it to be done by
mistake. It's certainly not the case that reflection gets "done by mistake".
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> 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.
Yes. Of course, it could just look like
alpha[i] = 32;
and you're equally screwed. Or even
&myInstance.update()
if my instance happened to be on the stack before but isn't now, and your
instance is there instead.
Tell me why you think reflection can be done by mistake?
> 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.
Well, yes, that's kind of the point.
> (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.)
Without reflection, you have no generalized way to handle this. With
reflection, you can write code that does the right thing. Of course this is
simplified.
But the same is true of a manually-implemented class.
And when you're serializing something to (for example) store as a hidden
field on a web page, chances are when you update the code whoever is in the
middle of a session is going to break anyway.
Or, when you change the shape, you fix the database appropriately.
> If you trash memory, you are writing non-portable, non-standard-conforming
> code. All bets are off.
You're making a mistake that is arguably easier to make than using a name
that starts with an underline.
That's exactly what I'm saying: If you make a mistake, all bets are off. So
you're counting on the perfection of every author and contributor to your
codebase to always do the write thing, because the compiler *doesn't* check
that it's right, and the runtime *doesn't* check that it's right.
So I'm not seeing why the compiler not checking for you intentionally using
a private variable is *less* encapsulated than the compiler not checking you
don't accidentally have a wild pointer. The "standards-compliant" Python
code says "don't dick with the private variables of an instance unless you
want code that's fragile when that class gets upgraded."
In my experience, I'd much rather use a language where the access rules are
enforced, even if those access rules allow well-defined methods of getting
at private variables, than to use a language where a mistake by one
programmer breaks code in a completely unrelated programmer's module. That's
just my experience, tho.
>> 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".
So you're saying having non-portable ways of bypassing the typing is better
than having portable ways of bypassing the typing? C# is less encapsulated
than C++, because C# has documented ways of intentionally getting to private
variables, but C++ has only accidental compiler-specific ways of getting at
private variables?
How about a declaration on a class that says "It's OK for someone to bypass
the private declarations and use reflection on this class", while classes
without that declaration would not be accessible from the reflection
methods. What do you think of that idea?
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> That's a hilariously bad example
I was assuming you're smart enough to generalize what I'm saying here.
>> 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?
If you could go thru the list of private variables and store the values,
then later reassign them to how they were, you're golden, yes?
>>> 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.
No, I really don't understand. You seem to be arguing that a universal
naming convention isn't sufficient to say you have encapsulation, but a lack
of ability to keep others from changing your private variables by accident
is not a hindrance to encapsulation.
You seem to be saying that it's "unmodular" to say you can only violate
encapsulation on purpose via complex library routines intentionally designed
for metaprogramming, but it's modular to let people violate encapsulation by
accident?
> If you write normal, standard-conforming C++, you can perfectly well
> enforce access rights.
Um, no, really, you can't. How do you prevent me from corrupting your
private variables with a wild pointer?
class Alpha {
int abc = 0;
public: int get_abc() { return abc; }
}
You're telling me it's impossible for anyone to write code that would ever
make an instance of Alpha ever return anything but zero when calling
get_abc()? Really??
If I do
Alpha beta;
memset(&beta, 33, 4);
std::cout << beta.get_abc();
I'm still guaranteed to get a 0 printed? How did I violate the standard?
By "standard-conforming", do you mean "never accidentally have undefined
behavior, like using a pointer after you've freed it"?
And the standard that you don't access other instances variables that start
with an underline doesn't count as "standard-conforming"?
Note that if by "standard-conforming" you mean "never making a mistake",
it's not *your* code that makes the mistake. It's someone else's. So now
you're back to trusting everyone to not make mistakes in order to enforce
encapsulation, aren't you? You can't enforce it on your own.
I'm really trying to understand here, ya know. I'm trying to get my head
around it. Maybe I've just been away from compiled languages too long or
something.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> (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.)
Actually, thinking about it, in this particular case of pushing something
out to a database, the usual method is to use reflection to create a class
based on the database schema, then instantiate objects and populate them
from the database. So changing the object means changing the schema, then
fixing the code that accesses the private variables of the resultant
instances so as to perform the business logic.
So your code for reading the schema might say "I'll create an object when
you read a row. That object will have a class whose name is row_xyz if the
table is named xyz. The fields will be col_abc and col_def if there are two
columns named abc and def. The types will map from SQL to C# (or whatever)
like so: ... I'll provide a routine called "load(...)" for each indexed
key, and one called "save()" that writes the object back and "del()" that
deletes the row from the database. You write the routines that use the
columns as you like."
So when you reorg the database using SQL, you automatically get a new class
without having to write all the code to generate SQL statements populated
with the values of private variables.
You could do this with a code generator, sure, but that's all reflection
stuff really is - using the compiler and runtime at runtime instead of just
compile time.
> 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
*I* don't write code like that at all. Others do, then blame me when their
bugs break the modularity of my code, making it misbehave. See the problem?
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> 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'm saying that C++ does not enforce that you don't change private instance
variables from outside the class.
There's a certain subset of valid C++ programs that don't change private
instance variables from outside the class, namely those that don't
accidentally invoke undefined behavior. And there's a valid subset of C#
programs that don't change instance variables from outside the class, namely
those that don't use the reflection libraries. Which do you think is harder
to inspect to see what type of program you have?
>>> 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?
Whichever one my code just accidentally clobbered. If
>
>> 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.
Nope. Read it again. What exactly gets set to 27? Do you think there's a
good chance it might by y?
> It enforces it as long as you don't deliberately try to bypass the
> compiler checks.
No. It enforces as long as you don't *accidentally* bypass the compiler
checks at runtime. That's my point! You *can* accidentally 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.
No. I'm saying that in many of these types of languages, including C#, you
can enforce the privacy except in cases where the caller goes to extreme
lengths to bypass that privacy. You don't seem to like the idea that
reflection violates encapsulation, but you seem perfectly happy with the
idea that out of bounds array indecies violate encapsulation, simply because
the standard doesn't say it enforces encapsulation in that case.
>> 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.
Only if you do it in the obvious way. Or are you saying there's a
compile-time warning for calling memset() with too large a size?
>> 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.
Sure you do. You have to put "private:" in front of the private bits and
"public:" in front of the public bits. In Python, you put "export" in front
of the list of names of public functions, and you don't put "export" in
front of the list of names of private functions.
>> Or, in short, you can't protect against bad programmers
> So let's deliberately make bad code?
No. You're assuming the C++ programmers only write standard-conforming code
and never accidentally (or maliciously) bypass the type system with a
runtime bug like a dangling pointer.
Yet you assume the programmer will use reflection to bypass encapsulation
without understanding the drawbacks, or accidentally use a well-marked
private variable, and thus encapsulation is broken.
It's a bit of a double-standard there, I think. That's why I'm a bit
confused. I think I'm seeing what you're saying: I think you're saying
"Ideally, you can't bypass C++ private on purpose in a portable and
standards-conforming way, while C# et al give you a standard way of doing
so." In my experience, this isn't nearly as helpful a distinction as you
seem to have experienced.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New wrote:
>> You are accessing a public variable, not a private one.
>
> Nope. Read it again. What exactly gets set to 27? Do you think there's
> a good chance it might by y?
Or, to phrase it differently, do you know of any C++ compiler that won't
clobber y with that code? Given that the layout of the class is required to
match the layout of the corresponding C structure (no virtual methods),
the only real choices would see "clobber y", "dump core", or "do something
completely unrelated to the C++ memory model."
... just to clarify. :-)
--
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
|
 |
|  |
|  |
|
 |
From: Nicolas Alvarez
Subject: Re: This is the sort of brokenness...
Date: 18 Mar 2009 21:49:22
Message: <49c1a4a1@news.povray.org>
|
|
 |
|  |
|  |
|
 |
Warp wrote:
> 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.
Instead of using private:, you could just add an underscore to internal
variables. It's not like someone would end up using one of those without
even noticing, so you don't necessarily have to write long documentation
explaining it. If someone used your "private" members, and his code breaks
when you rewrite your implementation, he gets to keep the pieces.
I'm just saying languages with no compiler- or runtime-enforced data hiding
aren't that bad. I'm definitely not suggesting people just add an
underscore when they're using a language that *does* have real private
data.
Post a reply to this message
|
 |
|  |
|  |
|
 |
From: Nicolas Alvarez
Subject: Re: This is the sort of brokenness...
Date: 18 Mar 2009 21:52:44
Message: <49c1a56c@news.povray.org>
|
|
 |
|  |
|  |
|
 |
Warp wrote:
> 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).
Tons of existing code are intentionally accessing stuff they know is subject
to change?
Post a reply to this message
|
 |
|  |
|  |
|
 |
From: Nicolas Alvarez
Subject: Re: This is the sort of brokenness...
Date: 18 Mar 2009 21:57:04
Message: <49c1a670@news.povray.org>
|
|
 |
|  |
|  |
|
 |
Warp wrote:
> 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.
If you're using an underscore-prefixed member in Python, you *know* it's
private. It's not "done by mistake".
Plus, those aren't in the documentation, so you'd have to read the source
code to figure out their names.
Post a reply to this message
|
 |
|  |
|  |
|
 |
From: Nicolas Alvarez
Subject: Re: This is the sort of brokenness...
Date: 18 Mar 2009 22:04:11
Message: <49c1a81b@news.povray.org>
|
|
 |
|  |
|  |
|
 |
Warp wrote:
> Why not? Having the compiler check for breaking of agreements (eg.
> "don't
> access these variables") is much better than oral agreements. The compiler
> is much better at catching mistakes than you are.
It's an underscore or something else clearly visible in the offending
(= private-data-using-) code, not an oral agreement.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Nicolas Alvarez wrote:
> If you're using an underscore-prefixed member in Python, you *know* it's
> private. It's not "done by mistake".
Actually, to use C++ terminology, that makes it protected. Two underscores
makes it private. :-)
--
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
|
 |
|  |
|  |
|
 |
From: Nicolas Alvarez
Subject: Re: This is the sort of brokenness...
Date: 18 Mar 2009 22:06:52
Message: <49c1a8bb@news.povray.org>
|
|
 |
|  |
|  |
|
 |
Darren New wrote:
> Warp wrote:
>> 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;
Undefined behavior.
> How about
> // Your code...
> class xyz {
> public: int[10] x;
> private int y = 0;
> }
>
> // My code
> xyz pdq;
> pdq.x[10] = 27;
Undefined behavior.
Post a reply to this message
|
 |
|  |
|  |
|
 |
From: Nicolas Alvarez
Subject: Re: This is the sort of brokenness...
Date: 18 Mar 2009 22:08:32
Message: <49c1a920@news.povray.org>
|
|
 |
|  |
|  |
|
 |
Warp wrote:
> Deliberately hacking yourself around the compiler checks requires more
> malice than using a public member variable which simply "shouldn't"
> access.
Deliberately accessing a private member through reflection is harder too.
And it's not "malice".
Deliberately accessing a private member by typing its name with the
underscore is not hard at all, but you'll know you're doing it, and if it
breaks it's your fault :)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Nicolas Alvarez wrote:
> Undefined behavior.
> Undefined behavior.
Certainly. And that's my point. The C++ standard doesn't say "you can't get
to private members of a class." It says "We're not going to tell you a
reliable and portable way of doing so." That doesn't mean you can't access
private variables - otherwise, we wouldn't have buffer overflows clobbering
private variables in exploits, would we?
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> > You are nitpicking, and you know it.
> No, I really don't understand. You seem to be arguing that a universal
> naming convention isn't sufficient to say you have encapsulation, but a lack
> of ability to keep others from changing your private variables by accident
> is not a hindrance to encapsulation.
> You seem to be saying that it's "unmodular" to say you can only violate
> encapsulation on purpose via complex library routines intentionally designed
> for metaprogramming, but it's modular to let people violate encapsulation by
> accident?
Actually I'm not saying that. I have nowhere saying that.
I started by defending data hiding principle in the object-oriented/modular
programming paradigm, and stressed that this principle is completely
independent of the language used.
Then somehow you started talking about C++ in particular and somehow the
conversation was drifted from a programming paradigm in general to the quirks
in C++ in particular. I have nowhere defended C++ in particular, but data
hiding in general, as a programming technique.
Your argument against this technique seems to be "since you can't completely
guard against it in C++, it's as good as not having data hiding at all". As
if C++ had anything to do with this issue.
> > If you write normal, standard-conforming C++, you can perfectly well
> > enforce access rights.
> Um, no, really, you can't. How do you prevent me from corrupting your
> private variables with a wild pointer?
If you write standard-conforming C++, you won't have a wild pointer
(unless you have a bug, of course).
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Nicolas Alvarez <nic### [at] gmail com> wrote:
> Instead of using private:, you could just add an underscore to internal
> variables.
But why would I want to do that? There's no benefit. There are only
potential disadvantages.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Nicolas Alvarez <nic### [at] gmail com> wrote:
> Warp wrote:
> > 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).
> Tons of existing code are intentionally accessing stuff they know is subject
> to change?
I still can't comprehend what's so bad in the compiler doing the check
that you don't access the private members.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> I disagree. Now you've got a naming convention that isn't enforced that
> would mean something to people trying to read your code.
You mean the naming convention *is* enforced by languages which do not
explicitly support private data?
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> > If you trash memory, you are writing non-portable, non-standard-conforming
> > code. All bets are off.
> You're making a mistake that is arguably easier to make than using a name
> that starts with an underline.
> That's exactly what I'm saying: If you make a mistake, all bets are off. So
> you're counting on the perfection of every author and contributor to your
> codebase to always do the write thing, because the compiler *doesn't* check
> that it's right, and the runtime *doesn't* check that it's right.
Your argumentation just doesn't make any sense.
The question here is: Are compiler-enforced private member variables a
good thing in programming or not?
Your argument why they are not: In C++ you can accidentally access them
regardless of the compiler checks.
I think that your problem is that you have some kind of holy war against
C++, and every single discussion about programming is always somehow turned
to bashing C++. Thus every time you want to discredit some argument, you
go and explain why that thing is done wrong in C++, regardless of whether
C++ in particular was being discussed, or whether it was a more generic
discussion about programming.
I defended data hiding in general, as a programming paradigm (and I made
that pretty clear). Your very first reply to me was a reference to (and
attack against) C++. You certainly didn't wait. Now you have succeeded in
dragging this thread away from the original question, and made it about
access rights in C++ and why everything is better in all other languages.
I'm honestly getting tired of your C++ tirades. Every single subject
related to programming must somehow include C++ bashing, regardless of
the subject. That's getting tiresome.
I'm also getting tired of you twisting what I say. I have admitted that
in C++ you can trash memory, triggering undefined behavior which might,
among other things, modify the state of an object. You somehow have managed
to twist this so that, according to you, I'm saying this is a good thing
(or somehow "better" than other means of accessing private members). I can't
understand where you are getting this. You are just arguing for the sake of
arguing. Example:
> > 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".
> So you're saying having non-portable ways of bypassing the typing is better
> than having portable ways of bypassing the typing?
How do you even manage to twist my words to almost the exact opposite of
what I'm saying?
Where exactly do you see the word "better", or any kind of synonym or
reference to it? That's completely your twisted invention.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Nicolas Alvarez <nic### [at] gmail com> wrote:
> Warp wrote:
> > Why not? Having the compiler check for breaking of agreements (eg.
> > "don't
> > access these variables") is much better than oral agreements. The compiler
> > is much better at catching mistakes than you are.
> It's an underscore or something else clearly visible in the offending
> (= private-data-using-) code, not an oral agreement.
Ok, "written agreement" then, if you want to nitpick. Not much difference.
Still not enforced nor sanity-checked by anyone or anything.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> Warp wrote:
> > 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'm saying that C++ does not enforce that you don't change private instance
> variables from outside the class.
And this has exactly what to do with whether (compiler-enforced) data
hiding is a good thing or not?
Every single discussion about programming with you turns into C++ bashing,
no matter what the subject.
> >> 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.
> Nope. Read it again. What exactly gets set to 27? Do you think there's a
> good chance it might by y?
You are accessing a public variable, namely the array named 'x'.
You are accessing it out of boundaries, triggering UB, after which the
compiler is free to do whatever it wants with your code. It may as well
draw an ascii mandelbrot for all the standard cares.
Could that perhaps change 'y'? Maybe. The standard allows the compiler
to do whatever it wants. If the compiler wants to change 'y', it has the
standard's blessing. If the compiler gives you an error message saying that
you are a moron, that's ok as well.
> >> 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.
> Only if you do it in the obvious way. Or are you saying there's a
> compile-time warning for calling memset() with too large a size?
I'm just baffled about how you twist words. Exactly where am I saying
anything like that? I don't even understand how you can come with such a
conclusion. It doesn't make any sense.
"The compiler doesn't enforce encapsulation, it just gives warnings."
"Actually the compiler gives an error if you try to break encapsulation."
"Are you saying there's a compile-time warning for calling memset wrongly?"
"???"
I just can't follow your logic.
> >> 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.
> Sure you do. You have to put "private:" in front of the private bits and
> "public:" in front of the public bits.
Exactly how is that "documentation"? That's source code.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> or the infamous "#define private public"
Btw, if you *really* want to hide your private variables, there's a
technique used by some:
class MyClass
{
public:
// public functions here
private:
struct Members;
Members* members;
};
If the struct 'Members' is implemented eg. in a static or dynamically
loadable library, ie. you don't have the source code, it's extremely
difficult for anyone to even figure out what members there are in there
or how to access them. Your #define won't help at all here, nor accessing
the object by other means.
You could, of course, take that pointer and start trashing the memory
it's pointing to, but that would be rather pointless. Certainly not useful.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> I started by defending data hiding principle in the object-oriented/modular
> programming paradigm, and stressed that this principle is completely
> independent of the language used.
We did kind of get into a bit of a flurry of messages there, didn't we?
Let's see. I think we started out with a comment that minimalist languages
with powerful syntax (e.g., LISP) can implement things like OO without
having it built in, by using closures for example. Then the subject of CLOS
came up (which AFAIK doesn't use closures), with an indication that it's
possible to access the private members with sufficient work.
You said
"""
I think that the typical impossibility or difficulty in data hiding in
languages which do not have specific support for modularity is a sign that
the "OOP" is, after all, just a kludge.
"""
and later talked about how reflection makes it impossible to hide private
variables and implying that clear and unambiguous naming conventions that
everyone who codes in the language understands aren't sufficient.
I have agreed with that. I think what we're disagreeing with is the degree
to which the language implementation needs to enforce that modularity
strictly, but if it does, that's better than a language that disallows it
without enforcing it, because you can look at your code and see what it's doing.
I prefer being able to break the modularity with reflection, because it
gives you lots of power you otherwise need to write a bunch of boilerplate
to handle.
Lacking that, I prefer to have the modularity enforced, so I can reason
logically about a piece of code and determine that it will work correctly
even if there are bugs in other parts of code. I've had too many occasions
where I spend days looking for bugs in my code only to find that someone
else had scribbled over memory. Or even, in one case, had printed up "now
invoking Darren's code"[1] and then crashing out while prepping the
arguments to my module.
Lacking that, you have glorified assembly language. :-) The machine
represented to your code matches the machine it's running on. There are
times when that's good, but my work lately has not been one of those times.
> Your argument against this technique seems to be "since you can't completely
> guard against it in C++, it's as good as not having data hiding at all". As
> if C++ had anything to do with this issue.
There were examples given as "private". I don't know who started it, maybe
not you. I was simply pointing out that it's as easy for someone to violate
your encapsulation in C++ as in LISP because the compiler doesn't enforce
encapsulation. You can't completely guard against it in C++, and you can't
completely guard against it in CLOS (the LISP OO system under consideration)
or Python. I don't see that big a difference, really.
>> Um, no, really, you can't. How do you prevent me from corrupting your
>> private variables with a wild pointer?
>
> If you write standard-conforming C++, you won't have a wild pointer
> (unless you have a bug, of course).
No. If *everyone* writes standard-conforming C++, you won't have a wild
pointer. In safe languages, it doesn't matter what you write, *I* can count
on my library behaving as I wrote it. And, unfortunately, the C++ compiler
may try to enforce "private", but it doesn't try to enforce
standard-conforming C++.
If you call "accessing someone else's private variables on purpose" a bug,
then you can't have standard-conforming Python that violates encapsulation
without a bug, either.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Nicolas Alvarez <nic### [at] gmail com> wrote:
>> Instead of using private:, you could just add an underscore to internal
>> variables.
>
> But why would I want to do that? There's no benefit. There are only
> potential disadvantages.
He's saying that putting "private:" before the variables in a class
declaration is equivalent to naming private variables with an underscore. In
the first case, the compiler warns you if you use a private variable by
mistake. In the second case, it's obvious from inspection. If you have
x._y
in your code, you're doing something wrong. :-)
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp escreveu:
> Nicolas Alvarez <nic### [at] gmail com> wrote:
>> Warp wrote:
>>> Why not? Having the compiler check for breaking of agreements (eg.
>>> "don't
>>> access these variables") is much better than oral agreements. The compiler
>>> is much better at catching mistakes than you are.
>
>> It's an underscore or something else clearly visible in the offending
>> (= private-data-using-) code, not an oral agreement.
>
> Ok, "written agreement" then, if you want to nitpick. Not much difference.
> Still not enforced nor sanity-checked by anyone or anything.
Here is where Larry Wall's quote really applies. ;)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>> I disagree. Now you've got a naming convention that isn't enforced that
>> would mean something to people trying to read your code.
>
> You mean the naming convention *is* enforced by languages which do not
> explicitly support private data?
Not exactly.
In Python (and, AFAIK, LISP), getting to private functions, for example,
requires you to explicitly name the module and private function, and say
"pull them into my space."
However, you can get to private members without doing that.
Therefore, the convention is that private members are given names that
indicate they're intended to be private. Private functions don't need that
naming convention, because there's a list out there in the source code that
distinguishes private from public. That list is only enforced by default,
but you can work around it if you care to read the code and see what isn't
exported, or if you grope into the generated code and look what's there.
Even if it were enforced in Python, the metadata is there to be used and
abused. You can go into the data structures that make up the class or the
instance and modify them, because that's exactly how the system works
normally. The compiler works by building those data structures at runtime,
so if the compiler can do it, so can you. That's why the original article
talked about dynamic systems, i.e., systems that you can change at runtime.
For example (and very simplified), in Python, each object has a hashtable
mapping the names of fields to their values. Each member object of each
class and instance is in a hashtable. The hashtable is called "__dict__", so
xyz.__dict__["hello"] is the same as xyz.hello. If you marked "hello" as
private (even if you could), code could still get to it via __dict__. If you
marked __dict__ as private, all kinds of meta stuff would break.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> He's saying that putting "private:" before the variables in a class
> declaration is equivalent to naming private variables with an underscore. In
> the first case, the compiler warns you if you use a private variable by
> mistake.
Could you give a concrete example of a compiler, of any OO language,
which will only *warn* (rather than give an error message) if you try to
access a member variable specifically marked as private from the outside?
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
nemesis <nam### [at] gmail com> wrote:
> > Ok, "written agreement" then, if you want to nitpick. Not much difference.
> > Still not enforced nor sanity-checked by anyone or anything.
> Here is where Larry Wall's quote really applies. ;)
Since when have code sanity checks become a bad thing?
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> The question here is: Are compiler-enforced private member variables a
> good thing in programming or not?
My argument, again, very specifically, is that there's a bunch of
possibilities, not two:
1) They're enforced 100%. Nobody outside the class is capable of writing to
or reading from a private variable. This provides maximum isolation at the
expense of not having available at runtime lots of stuff the compiler knows
at compile time that you might want to know (e.g., reflection type stuff).
The benefit is you can debug modules in isolation.
2) They're enforced 100%, but people outside the class can read but not
write them. This is a little more fragile to change, but still capable of
being debugged in isolation, because other parts of the code can't break
your invariants. Eiffel does this, but it's the same syntax (x.y) to
reference the y member of x as it is to invoke the nullary member function y
on x, so you don't need to do more than recompile the client code. (I think
you can also say a variable is completely private, IIRC.)
3) They're enforced, but there are explicit mechanisms to bypass them, such
as reflection. This is my personal preference, because it's easy to find the
places in the code that might be breaking your invariants or might rely on
your internal implementation, while providing the power of metadata. This is
the C# and Java model.
4) They're enforced by the compiler but not by the runtime. This removes
*both* the ability to debug your module in isolation *and* the ability to do
metaprogramming that eliminates boilerplate by using the information the
compiler has already calculated.
5) They're not enforced by the compiler or the runtime, but there are
conventions and/or standards that make it obvious to everyone when you're
breaking the data encapsulation, and the runtime ensures that you can only
do this "on purpose". That is, the unenforced conventions (or enforced but
bypassable mechanisms) ensure that the only way of breaking encapsulation is
on purpose. This is almost as good as #3, except it may be harder to track
down who is violating your invariants.
6) There's no convention, and all you have is documentation saying which
bits are supposed to be private and which aren't. Worst of all possible
worlds. Not very modular at all.
> I think that your problem is that you have some kind of holy war against
> C++, and every single discussion about programming is always somehow turned
> to bashing C++.
Nope. Someone brought up C++ by talking about the compiler enforcing
"private:". I was just pointing out that the compiler only enforces it in
some ways, and not in the ways that I thought was also important for modularity.
I.e., there's two good reasons for modularity: future-proofing your code,
and bug-proofing your code. You only talked about the former.
> I defended data hiding in general, as a programming paradigm (and I made
> that pretty clear). Your very first reply to me was a reference to (and
> attack against) C++.
Err, no it wasn't. I simply said "LISP can use macros to simulate OO just
like C++ uses constructors and destructors to do resource management."
> You certainly didn't wait.
Sure I did. Go back and look what I said about C++ again. Really.
> access rights in C++ and why everything is better in all other languages.
I haven't said better. I said different. I'm talking about the things above,
which have little to do with C++ except it's the only unsafe OO language I
know of.
> I'm honestly getting tired of your C++ tirades. Every single subject
> related to programming must somehow include C++ bashing, regardless of
> the subject. That's getting tiresome.
I'm really not trying to bash C++ here. If pointing out that C++ allows you
to corrupt member variables with a bug is "bashing", then I guess I'm bashing.
>> So you're saying having non-portable ways of bypassing the typing is better
>> than having portable ways of bypassing the typing?
>
> How do you even manage to twist my words to almost the exact opposite of
> what I'm saying?
>
> Where exactly do you see the word "better", or any kind of synonym or
> reference to it? That's completely your twisted invention.
OK. I'm just trying to communicate here. That's why I'm asking the question.
It seemed to me that you preferred a language with unsafe behavior and
compiler-enforced checks to one with safe behavior and no compiler-enforced
checks. I was asking whether that was the case. The right answer would be
"No, that's worse" or "No, that's different but just as bad because..." or
something like that.
I've never heard you call C++ a kludge OO language. I assumed you were
excluding C++ from that criticism when you said a language that allows
access to private members is a kludge OO.
AFAIK, using lambdas to implement OO (which you can do in Python or LISP) is
the only mechanism I've seen that doesn't expose private instance variables
outside the class. Everything else provides either reflection or undefined
(or even well-defined) behavior to so do.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> I still can't comprehend what's so bad in the compiler doing the check
> that you don't access the private members.
If it's actually enforced (as in an error rather than a warning), it makes
lots of things harder than they need to be, because the running code winds
up with less information than the compiler had. You're throwing away half
the work the compiler did once you generate the code, so you wind up with
lots of boilerplate to enter into code things the compiler already knows.
If it's not enforced (i.e., it's a warning), then I don't see it as an
improvement over a naming convention. People don't accidentally use x._y in
their python code any more than they accidentally name something
__builtin_printf in their C code. In CLOS, you actually have to go read the
source code of the implementation of the class to see what member variables
you can access, so it's not like you don't know you're violating encapsulation.
If it's *really* enforced (as in you actually can't get to private
variables), it lets you do things like prove your program is correct and
lets you do things like know you've got a bug in your module when an
instance variable winds up with the wrong value.
If it's half-enforced, as in the compiler complains and won't compile the
code, but there's ways to get around it anyway (on purpose or by mistake),
then it's IMO the worst of all possible worlds. You'll spend hours or days
trying to debug code that's already right because the client is convinced
the other code they've written is bugfree and it's easier to blame you than
to find the wild pointer in their own code. The whole idea of class
invariants goes out the window.
Incidentally, I see little wrong with breaking encapsulation if you maintain
the invariants. It makes it harder to upgrade in the future without changing
the client, but that's the price you pay for it. If you can automate the
access to where it doesn't hurt to change the client, it seems like a
win-win to me.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> You could, of course, take that pointer and start trashing the memory
> it's pointing to, but that would be rather pointless. Certainly not useful.
Sure. And that's how C does its OO design pattern. :-)
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>> He's saying that putting "private:" before the variables in a class
>> declaration is equivalent to naming private variables with an underscore. In
>> the first case, the compiler warns you if you use a private variable by
>> mistake.
>
> Could you give a concrete example of a compiler, of any OO language,
> which will only *warn* (rather than give an error message) if you try to
> access a member variable specifically marked as private from the outside?
Sure. C# or Java. You get this warning that says "this won't compile." So
you use the reflection libraries instead, and it compiles. The compiler
hasn't stopped you from accessing the private variable. It just stopped you
from trivially accidentally accessing the private variable. (Python doesn't
even prevent you from trivially accessing the private variable, but in
practice it isn't a problem.)
I know what you're asking. I'm asking you to look at it from a slightly
different point of view. Take a more general approach to what it means to
get "a warning" than the usual "the compiler issues warnings and errors, and
nothing else is meaningful."
Another example is, as I've been saying, C++. The compiler warns (and won't
compile) if you access the private variable by name, but not if you
intentionally or accidentally access the private variable by address. Again,
I understand what you're saying, and I'm asking you to also try to
understand what I'm saying, instead of thinking I'm only saying it to annoy
you. This is really how I think about programming - what do I know, and what
might be broken. That's why I dislike unsafe languages with undefined behavior.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>> Warp wrote:
>>> 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'm saying that C++ does not enforce that you don't change private instance
>> variables from outside the class.
>
> And this has exactly what to do with whether (compiler-enforced) data
> hiding is a good thing or not?
Because there's two reasons for data hiding: (1) allow more flexibility in
implementation, (2) allow easier debugging.
If you can violate my encapsulation, I don't see the data as especially
encapsulated, is all.
> Every single discussion about programming with you turns into C++ bashing,
> no matter what the subject.
I'm happy to bash any other unsafe OO language you might want to name. :-)
> Could that perhaps change 'y'? Maybe. The standard allows the compiler
> to do whatever it wants. If the compiler wants to change 'y', it has the
> standard's blessing. If the compiler gives you an error message saying that
> you are a moron, that's ok as well.
Yep. Can you name a C++ implementation that doesn't change "y" in that
sample? I'm 100% serious here, because I know of C interpreters, but I've
never seen a C++ interpreter. Or is there a tool that would catch that at
runtime? It would make some sorts of debugging a lot easier. I don't think
purify handles that sort of thing, does it? It seems like it would be
difficult, because the size of pointers would have to change or the shape of
the structure would have to be incompatible with C.
>>>> 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.
>
>> Only if you do it in the obvious way. Or are you saying there's a
>> compile-time warning for calling memset() with too large a size?
>
> I'm just baffled about how you twist words. Exactly where am I saying
> anything like that? I don't even understand how you can come with such a
> conclusion. It doesn't make any sense.
I'm used to thinking about the semantics of a program. It's easy to
miscommunicate when the semantics of a language include "there are no
semantics for this valid piece of code that compiles cleanly."
It's untrue that "trying to access a private variable gives a compiler
error." It's only true if you try to access the private variable in the
obvious way. It's not true if you explicitly or accidentally circumvent the
runtime system.
> "The compiler doesn't enforce encapsulation, it just gives warnings."
> "Actually the compiler gives an error if you try to break encapsulation."
> "Are you saying there's a compile-time warning for calling memset wrongly?"
> "???"
>
> I just can't follow your logic.
I said "the compiler warns you in some cases that you're accessing private
variables of other classes, and it doesn't warn you in other cases that
you're accessing private variables of other classes." You want to make that
into a discussion about whether it's a "warning" or an "error".
The compiler only gives you error messages if you break encapsulation in
*some* ways, not in other ways. I.e., only when you break encapsulation on
purpose does it give you a warning. When you break encapsulation by mistake,
it doesn't. If you access a private variable by name, it won't compile. If
you access a private variable by address, it compiles fine.
When you break encapsulation in CLOS on purpose, you don't get a warning,
but you know you're doing it because you're groping around in the source
code of the class you're violating. I don't see how making that uncompilable
can help.
Explain that to me? Really, without treating it as an attack, explain to me
how requiring a programmer to read the source code (not header) of the
module to discover the names of private variables isn't sufficiently
encapsulated, compared to something enforced at compile-time but not
runtime? It's discussions like this that lead me to a better and deeper
understanding of the craft. :-)
>> Sure you do. You have to put "private:" in front of the private bits and
>> "public:" in front of the public bits.
>
> Exactly how is that "documentation"? That's source code.
And in CLOS, it's also source code. The only way to break encapsulation is
go read the source code to see what the private variables are. It's not
something you do accidentally, any more than you accidentally
#define private public.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> nemesis <nam### [at] gmail com> wrote:
>>> Ok, "written agreement" then, if you want to nitpick. Not much difference.
>>> Still not enforced nor sanity-checked by anyone or anything.
>
>> Here is where Larry Wall's quote really applies. ;)
>
> Since when have code sanity checks become a bad thing?
When they become something to work around, rather than a help to the
programmer. If you need to go read the source code of the module you're
accessing to figure out how to get to the private variables, it's no longer
a "code sanity check" IMO.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New escreveu:
> Warp wrote:
>> nemesis <nam### [at] gmail com> wrote:
>>>> Ok, "written agreement" then, if you want to nitpick. Not much
>>>> difference.
>>>> Still not enforced nor sanity-checked by anyone or anything.
>>
>>> Here is where Larry Wall's quote really applies. ;)
>>
>> Since when have code sanity checks become a bad thing?
>
> When they become something to work around, rather than a help to the
> programmer. If you need to go read the source code of the module you're
> accessing to figure out how to get to the private variables, it's no
> longer a "code sanity check" IMO.
Err... I think here I'll have to agree with Warp. Why would you try to
bypass the "code sanity checks" and all the paranoid safety devices to
access something you shouldn't be accessing in the first place?
This thread has long derailed into something quasi-metaphysical... :P
My final word here is: to have modularity you don't need all the traps,
contraption devices and alarmist chainballs with all the barroque syntax
associated. All you need is to agree to use only what a module provides
you. It's your choice to be dumb enough or a smartass h4x0r and rip the
contract just to show you can.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
nemesis wrote:
> Darren New escreveu:
>> Warp wrote:
>>> nemesis <nam### [at] gmail com> wrote:
>>>>> Ok, "written agreement" then, if you want to nitpick. Not much
>>>>> difference.
>>>>> Still not enforced nor sanity-checked by anyone or anything.
>>>
>>>> Here is where Larry Wall's quote really applies. ;)
>>>
>>> Since when have code sanity checks become a bad thing?
>>
>> When they become something to work around, rather than a help to the
>> programmer. If you need to go read the source code of the module
>> you're accessing to figure out how to get to the private variables,
>> it's no longer a "code sanity check" IMO.
>
> Err... I think here I'll have to agree with Warp. Why would you try to
> bypass the "code sanity checks" and all the paranoid safety devices to
> access something you shouldn't be accessing in the first place?
Because, every once in a while, the provider of the library wasn't omniscient.
It's pretty simple. It's a practical thing. Say you have (just for an
example) a video codec. It does all kinds of nice things with timestamps and
such, but one thing it doesn't provide is telling you the current frame
number. However, that is stored in a private variable (which you can see
from reading the .h file), and for some reason you need that information to
implement your player in an environment the original author didn't intend
his player to be used in.
You have three choices:
1) Violate encapsulation, and pay the price if you ever need to use a new
version of the decoder that doesn't have the frame number in the same place.
2) Rewrite the entire video decoder from scratch.
3) Find the owner of the original code and get him to change it and agree to
support it indefinitely.
It's a cost vs benefit thing.
You "shouldn't" be accessing the internal data structures NTFS stores on the
disk. It makes it kind of hard to make an open source NTFS driver without
doing so. If your systems *really* prevented that access, such drivers would
be impossible to write. When MS changes the NTFS drivers, Linux maintainers
have to go in, reverse engineer things again, and fix stuff up to stay
compatible. That's a cost that wouldn't be borne if Microsoft's NTFS was
open source and there was only one code base.
Sure, if you have the source code to the module, providing public access to
the private variable is pretty easy. I'd not recommend bypassing the
protection mechanisms in that case. I thought that would be obvious to
people, tho, so I assumed everyone was on the same page with me there.
(Which is not to say anyone disagrees or has disagreed with that here.)
> This thread has long derailed into something quasi-metaphysical... :P
I'm thinking it's something very practical. I'm not advocating regularly
accessing the internals of other objects. I'm just saying that preventing it
by convention or by hiding information from the humans isn't any worse IMO
than implementing it in the compiler.
> My final word here is: to have modularity you don't need all the traps,
> contraption devices and alarmist chainballs with all the barroque syntax
> associated. All you need is to agree to use only what a module provides
> you.
And to be able to tell easily when you're violating it. If you have to read
prose documentation to know whether some routine or variable is private or
public, *then* you lack modularity.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New escreveu:
> nemesis wrote:
>> Err... I think here I'll have to agree with Warp. Why would you try
>> to bypass the "code sanity checks" and all the paranoid safety devices
>> to access something you shouldn't be accessing in the first place?
>
> Because, every once in a while, the provider of the library wasn't
> omniscient.
You can also try another library. ;)
> And to be able to tell easily when you're violating it. If you have to
> read prose documentation to know whether some routine or variable is
> private or public, *then* you lack modularity.
Speaking purely from the point of view of Scheme and functional
programming, there's no such a fuss: all the module provides are public
functions. You just read their names and parameters and use them.
You can see from this thread that while it began with CLOS, it ended in
C++ because of its tons of private, protected, friend and other
schizophrenic access mechanisms leading to complicated interfaces and
behavior for modules.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
nemesis wrote:
>> Because, every once in a while, the provider of the library wasn't
>> omniscient.
>
> You can also try another library. ;)
Well, yeah.
> You can see from this thread that while it began with CLOS, it ended in
> C++ because of its tons of private, protected, friend and other
> schizophrenic access mechanisms leading to complicated interfaces and
> behavior for modules.
Actually, I thought it ended in C++ because that's the only unsafe OOP
language I know of. It's the only language I know where the compiler
attempts to enforce modularity but in practice fails to do so, by declaring
violation of modularity "undefined" or "compiler-specific" rather than
catching all cases of broken modularity at compile time or runtime.
I was trying to figure out how well-documented modularity breaking is a
"kludge" but poorly documented unsupported modularity breaking isn't.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> > Could you give a concrete example of a compiler, of any OO language,
> > which will only *warn* (rather than give an error message) if you try to
> > access a member variable specifically marked as private from the outside?
> Sure. C# or Java. You get this warning that says "this won't compile."
Wait. The compiler gives a *warning* which says "this won't compile"?
If it's just a warning, and not an error, it means that it will compile
it anyways? Isn't that contradictory?
> Another example is, as I've been saying, C++. The compiler warns (and won't
> compile) if you access the private variable by name
I get no warning if I try to access a private member. I get an error.
There's a big categorical difference.
>, but not if you
> intentionally or accidentally access the private variable by address.
You can't access the private variable by address. Not according to the
standard. If you can, then that can be considered a non-standard compiler
extension.
And trashing memory, triggering UB, is not the same thing as "accessing
a member variable". The effect may be the same as accessing in one compiler,
but might not be in another. You are not *really* accessing it. You are
just abusing compiler and system specific undefined behavior.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> The question here is: Are compiler-enforced private member variables a
> good thing in programming or not?
>
> Your argument why they are not: In C++ you can accidentally access them
> regardless of the compiler checks.
Actually, let me ask this. Since you seem to be saying that reflection means
that an OO system is a "kludge" because it breaks modularity, can you tell
me what language *you* think isn't a kludge in this respect? Since the
discussion mentioned "private:" and you defended the practice, I may have
wrongly assumed that indicated that you thought C++ wasn't a kludge OOPL in
this respect.
I don't know of any language that actually enforces modularity to the extent
where it's impossible to write code that depends on private variables of
another class, especially one to which you have the source. Every
language[1] where the trivial ways of doing this (e.g., reading the source
and using private names from outside the class) are enforced also has the
ability to bypass it without too much trouble (either by using reflection or
pointer arithmetic).
I don't know of any languages that make it trivially easy to *accidentally*
confuse private and public member variables, except *perhaps* PHP and Perl,
both of which I'll happily admit are kludges. :-) They might not make it
trivial, I don't remember, but if they do, I'm not surprised.
[1] except perhaps Ada - I'd have to go look closer to see if unchecked
conversions can do that sort of thing on tagged records.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> Warp wrote:
> > I still can't comprehend what's so bad in the compiler doing the check
> > that you don't access the private members.
> If it's actually enforced (as in an error rather than a warning), it makes
> lots of things harder than they need to be, because the running code winds
> up with less information than the compiler had. You're throwing away half
> the work the compiler did once you generate the code, so you wind up with
> lots of boilerplate to enter into code things the compiler already knows.
That's the whole idea in modular thinking: You don't know what's inside
the module, and you *don't care*. You don't need to care.
As soon as you start worrying what's inside the module, you are already
breaking module boundaries and abstraction.
> If it's half-enforced, as in the compiler complains and won't compile the
> code, but there's ways to get around it anyway (on purpose or by mistake),
> then it's IMO the worst of all possible worlds. You'll spend hours or days
> trying to debug code that's already right because the client is convinced
> the other code they've written is bugfree and it's easier to blame you than
> to find the wild pointer in their own code. The whole idea of class
> invariants goes out the window.
No you'll explain to me how a naming convention of public variables helps
this problem.
> Incidentally, I see little wrong with breaking encapsulation if you maintain
> the invariants. It makes it harder to upgrade in the future without changing
> the client, but that's the price you pay for it. If you can automate the
> access to where it doesn't hurt to change the client, it seems like a
> win-win to me.
IMO if anyone feels the need to break your interface and access private
members directly, then your class design sucks and should be redesigned.
If your class design is good, nobody will need to access anything
directly.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> 1) They're enforced 100%. Nobody outside the class is capable of writing to
> or reading from a private variable. This provides maximum isolation at the
> expense of not having available at runtime lots of stuff the compiler knows
> at compile time that you might want to know (e.g., reflection type stuff).
If you need to know the private members of the class from the outside,
then that class has been badly designed.
With a well-designed class you don't *need* to know, nor care.
> 2) They're enforced 100%, but people outside the class can read but not
> write them. This is a little more fragile to change
It breaks modularity badly. The whole idea of data hiding is that the
private part should be completely opaque to the outside. The second it
isn't, outside code will start making assumptions, and you can't change
the implementation of the class anymore.
> but still capable of
> being debugged in isolation, because other parts of the code can't break
> your invariants. Eiffel does this, but it's the same syntax (x.y) to
> reference the y member of x as it is to invoke the nullary member function y
> on x, so you don't need to do more than recompile the client code. (I think
> you can also say a variable is completely private, IIRC.)
Some people argue that accessors to all private members, especially if
they are automatically generated, is almost as bad as having all the
members public. It exposes the internal implementation, and thus outside
code will start making assumptions about it, making it harder to change
the implementation later.
Some accessors may be justifiable, but you should not go overboard.
> 3) They're enforced, but there are explicit mechanisms to bypass them, such
> as reflection. This is my personal preference, because it's easy to find the
> places in the code that might be breaking your invariants or might rely on
> your internal implementation, while providing the power of metadata. This is
> the C# and Java model.
If you don't have access to the code which is making assumptions about
your class' internal structure, you can't change your class without breaking
that existing code.
Sure, this situation might not be extremely common, but it can happen.
And it's something which might have been avoided with a good interface
design and a completely opaque private implementation.
> 4) They're enforced by the compiler but not by the runtime. This removes
> *both* the ability to debug your module in isolation *and* the ability to do
> metaprogramming that eliminates boilerplate by using the information the
> compiler has already calculated.
Why would you want runtime checks if access rights could be fully checked
at compile time?
> 5) They're not enforced by the compiler or the runtime, but there are
> conventions and/or standards that make it obvious to everyone when you're
> breaking the data encapsulation, and the runtime ensures that you can only
> do this "on purpose". That is, the unenforced conventions (or enforced but
> bypassable mechanisms) ensure that the only way of breaking encapsulation is
> on purpose. This is almost as good as #3, except it may be harder to track
> down who is violating your invariants.
Again, if you must keep "reverse compatibility", for a lack of a better
term (in other words, you as a library developer must maintain compatibility
with existing code which uses your library, and you don't have access to
this existing code nor can change it), it can be a problem if the existing
code can make assumptions about your library and does so.
> > I think that your problem is that you have some kind of holy war against
> > C++, and every single discussion about programming is always somehow turned
> > to bashing C++.
> Nope. Someone brought up C++ by talking about the compiler enforcing
> "private:". I was just pointing out that the compiler only enforces it in
> some ways, and not in the ways that I thought was also important for modularity.
You immediately assumed that it must be talking about C++, of course,
and immediately jumped at the opportunity of bashing it.
> > I'm honestly getting tired of your C++ tirades. Every single subject
> > related to programming must somehow include C++ bashing, regardless of
> > the subject. That's getting tiresome.
> I'm really not trying to bash C++ here. If pointing out that C++ allows you
> to corrupt member variables with a bug is "bashing", then I guess I'm bashing.
In a discussion about the modular programming paradigm and data hiding you
succeeded in creating an lengthy thread about how you can trash memory in C++.
It's not exactly like you avoided the subject.
> OK. I'm just trying to communicate here. That's why I'm asking the question.
> It seemed to me that you preferred a language with unsafe behavior and
> compiler-enforced checks to one with safe behavior and no compiler-enforced
> checks.
That "you preferred a language with unsafe behavior" is 100% your own
invention. I have nowhere said that.
What I have said is that I prefer enforced modularity over non-enforced
one (which IMO is not modularity at all). That "unsafe" bit is all your
own twisting.
> I've never heard you call C++ a kludge OO language. I assumed you were
> excluding C++ from that criticism when you said a language that allows
> access to private members is a kludge OO.
At least you admit you are making assumptions.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
nemesis <nam### [at] gmail com> wrote:
> You can see from this thread that while it began with CLOS, it ended in
> C++ because of its tons of private, protected, friend and other
> schizophrenic access mechanisms leading to complicated interfaces and
> behavior for modules.
No. You just listed the excuses, not the real reason. The real reason
is that some people here really like to bash C++ at every possible
opportunity they can get. It's like a hobby to them. Or an obsession.
I certainly didn't bring up C++. It's only after the bashing started
that I got dragged into the discussion about it.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> >> I'm saying that C++ does not enforce that you don't change private instance
> >> variables from outside the class.
> >
> > And this has exactly what to do with whether (compiler-enforced) data
> > hiding is a good thing or not?
> Because there's two reasons for data hiding: (1) allow more flexibility in
> implementation, (2) allow easier debugging.
> If you can violate my encapsulation, I don't see the data as especially
> encapsulated, is all.
You didn't answer my question.
"Is enforced data hiding a good thing or a bad thing?"
"In C++ you can bypass the encapsulation, so it's a bad thing."
That doesn't make any sense. Exactly how is C++ related to the subject?
You are badly derailing.
But of course. As long as you get to bash C++...
> > Every single discussion about programming with you turns into C++ bashing,
> > no matter what the subject.
> I'm happy to bash any other unsafe OO language you might want to name. :-)
It's getting really tiresome and old.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> Actually, let me ask this. Since you seem to be saying that reflection means
> that an OO system is a "kludge" because it breaks modularity
In fact I said that trying to emulate OOP in a language which has no
direct support for OOP is a kludge. The only thing I said about reflection
is that IMO it breaks modularity and thus you lose many of the benefits of
modular design.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> Actually, I thought it ended in C++ because that's the only unsafe OOP
> language I know of.
I can't understand if that means "I haven't studied unsafe OOP languages
other than C++" or "to my knowledge there are no other unsafe OOP languages".
> It's the only language I know where the compiler
> attempts to enforce modularity but in practice fails to do so, by declaring
> violation of modularity "undefined" or "compiler-specific" rather than
> catching all cases of broken modularity at compile time or runtime.
Then you don't know much, do you.
> I was trying to figure out how well-documented modularity breaking is a
> "kludge" but poorly documented unsupported modularity breaking isn't.
The connection between my usage of the word "kludge" and your usage
of the words "unsafe" and "reflection" is purely your invention.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>>> Could you give a concrete example of a compiler, of any OO language,
>>> which will only *warn* (rather than give an error message) if you try to
>>> access a member variable specifically marked as private from the outside?
>
>> Sure. C# or Java. You get this warning that says "this won't compile."
>
> Wait. The compiler gives a *warning* which says "this won't compile"?
> If it's just a warning, and not an error, it means that it will compile
> it anyways? Isn't that contradictory?
No. That's why I'm saying you have to look at it from a slightly different
perspective. It's a compiler error to access the private variable the *easy*
way, just like in C++. It's not a compiler error to access the private
variable in the *difficult* way. Hence, you are *warned* when accessing the
private variable that you have to go out of your way to do it a different
way. The modularity isn't enforced at compile time.
>> Another example is, as I've been saying, C++. The compiler warns (and won't
>> compile) if you access the private variable by name
>
> I get no warning if I try to access a private member. I get an error.
> There's a big categorical difference.
To you. Not to me. There are compiler flags that change warnings to errors.
Does that mean they're not warnings any more?
>> , but not if you
>> intentionally or accidentally access the private variable by address.
>
> You can't access the private variable by address. Not according to the
> standard. If you can, then that can be considered a non-standard compiler
> extension.
// So....
class xyz {
int i;
} pdq;
memset(&pdq,23,sizeof(int));
// That's illegal C++ code?? I thought C++ is compatible with C?
// Surely the compiler has enough info there to refuse to compile
// it if it's illegal? Doesn't a class with no virtual members have
// to have the same memory layout as in C? How do you pass a struct
// to read(2) or write(2) if you can't take its address?
> And trashing memory, triggering UB, is not the same thing as "accessing
> a member variable".
I'm not saying I'm "trashing memory". I agree it's triggering UB, but that,
IMO, means C++'s encapsulation is as much of a "kludge" as CLOS's is. I
mean, if you're agreeing that C++'s OOP is a kludge also due to lack of
encapsulation, then sure, every OOP I know of is a kludge under those
definitions. They're just kludgey in different ways.
I'd rather have the kind where you have to intentionally violate
encapsulation in a way that is portable and makes it easy to see who is
doing it, than have the kind that a simple bug can violate encapsulation in
a module utterly unrelated to where the bug is.
And it's not about C++ vs other things, it's about safe vs unsafe languages.
The only reason I brought up C++ is it's the only language I know that
*doesn't* give you a well-defined way to work around the modularity in times
of need.
What OO language besides C++ do you think doesn't have a kludgey modularity
system? Any?
> The effect may be the same as accessing in one compiler,
> but might not be in another. You are not *really* accessing it. You are
> just abusing compiler and system specific undefined behavior.
And using reflection isn't *really* accessing it? Or is it? (That's a
question of your opinion, not a rebuttal.)
As I said, I see what you're saying. I'm trying to generalize a bit more.
Unsafe languages don't provide encapsulation of modules, IMO. The best they
do is to prevent you from accessing private variables in a straightforward
easy to understand way, without preventing you from accessing them in a
contorted and non-portable way. And since you're accessing the internals
of someone else's code anyway, you've already decided to take on the problem
of changing your access routines when the module changes, so the fact that
it isn't portable when you change compilers doesn't seem like that much of a
drawback to me, if the need arises.
It seems to be perfectly valid to use memcpy to copy the contents of an
Alpha class into an array of characters that's sizeof(Alpha) and then pull
out from that the values of any variables I need without violating the
standard. It's non-portable, because I need to change the access routines
when I change compilers, but ... I know that.
I wouldn't do that sort of thing if I had a choice. Certainly I wouldn't
publish source code that intentionally takes advantage of this sort of
undefined behavior. :-) But then, I wouldn't publish source code that
depends on the internals of someone else's CLOS code either.
Maybe here's something we can both agree on: Violating modularity can be a
kludge. That doesn't mean that mechanisms to allow that give you a kludgey
language. I'll grant that a language that makes it hard to know you're
violating encapsulation is a kludgey language in that respect. I'll grant
that any program that accesses private C++ variables or private CLOS
variables from outside their class is a kludge. I think we'll have to agree
to disagree on whether the mechanisms that *allow* such access (such as
reflection) are inherently kludges.
IMO, it also doesn't mean that every violation of modularity is a kludge. If
your violation of modularity doesn't violate the class invariant and it
automatically adjusts to new releases of the class you're violating without
further programming, I don't consider that a kludge. (Otherwise, for
example, debuggers would be "kludges" by definition, and I don't think
that's a reasonable assessment.)
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>> Warp wrote:
>>> I still can't comprehend what's so bad in the compiler doing the check
>>> that you don't access the private members.
>
>> If it's actually enforced (as in an error rather than a warning), it makes
>> lots of things harder than they need to be, because the running code winds
>> up with less information than the compiler had. You're throwing away half
>> the work the compiler did once you generate the code, so you wind up with
>> lots of boilerplate to enter into code things the compiler already knows.
>
> That's the whole idea in modular thinking: You don't know what's inside
> the module, and you *don't care*. You don't need to care.
It also prevents code from *inside* the module from doing certain things in
an easy way.
> As soon as you start worrying what's inside the module, you are already
> breaking module boundaries and abstraction.
Not if you're doing it from inside the module.
>> If it's half-enforced, as in the compiler complains and won't compile the
>> code, but there's ways to get around it anyway (on purpose or by mistake),
>> then it's IMO the worst of all possible worlds. You'll spend hours or days
>> trying to debug code that's already right because the client is convinced
>> the other code they've written is bugfree and it's easier to blame you than
>> to find the wild pointer in their own code. The whole idea of class
>> invariants goes out the window.
>
> No you'll explain to me how a naming convention of public variables helps
> this problem.
The naming convention doesn't help that. The safety of the language helps
that. In other words, what helps it is not the naming convention, but the
fact that code that accesses private variables *must* use the naming
convention to do so.
Then you can take the Python code you're running, and look for the string
"._framenumber". If you don't find that, nobody outside your class is
changing the framenumber, even by accident. (Simplified of course, but
that's the idea.)
In the CLOS case, you can just look for the name of the private variable
that you suspect is getting changed from outside. Or you can change the
source code you have to consistently rename that private variable and run it
again and get a useful indication of exactly where in the outside code it's
violating your encapsulation.
In an unsafe language, I can have code that was compiled before I even wrote
my class accidentally violate my class's encapsulation, and it'll be almost
impossible to figure out what is going on if the program's big enough or if
I don't have the source to that code.
I'm just less worried about intentionally violating modularity than
accidentally doing so. When I do it on purpose, I know the cost/benefit
trade-offs. Doing so may be a kludge, but there's reasons kludges get
implemented in the commercial world.
(I've had server code written in unsafe languages where it would dump core
on a segfault, and the providers wanted to blame it on the fact that the Xen
kernel was on the machine. We weren't booting Xen, but they wouldn't even
look at their code until we uninstalled that kernel. Just as an example of
how people can avoid admitting their own bugs might be part of the problem.
Another time, the "calculation" part of the program did its work, clobbered
memory, wrote on the screen "now generating report", tried to create the
data file for the report generator, and crashed out in a way that locked up
the machine. As the author of the report generator, I got to spend several
*weeks* trying to figure out what it was before I discovered it was just the
calc routines not knowing their own calling structure and hence returning to
the wrong place on the stack at some point. This included printing out *all*
of the code I'd written, about a 5" stack, and going thru line by line
looking for bugs. (Found one, which wasn't the problem. :-) Another problem
that wouldn't have happened in a safe language.)
If you're in a particularly hostile environment, the best way is to have
well-defined class contracts in a safe environment that prevents the abusers
from bypassing those contracts. If you're not in a hostile environment, you
just get the author to agree to support revealing the private information
you need.
> IMO if anyone feels the need to break your interface and access private
> members directly, then your class design sucks and should be redesigned.
I agree. That's why I say that CLOS allowing you to get to private members
from outside the class *if* you read the source code to the class and know
what they're called isn't, in practice, a problem. The only time someone
would do that is if for some reason they can't *change* the source (because,
perhaps, they're expecting an update), but they can't get the author to make
an update that provides the interface that someone needs. At which point the
only choice is (1) toss the entire module (rewrite, buy different version,
etc), or (2) take on the burden of staying compatible with updates (which
may be infrequent enough that it's in practice not a problem). It's a
cost/benefit sort of thing.
> If your class design is good, nobody will need to access anything
> directly.
I also agree. Or, at least, nobody will access anything directly without the
author's "permission". If I say "it's OK to serialize this object, send it
over the wire, and reconstitute it in another address space", I'd still say
that's a good class design. If I'm worried about it getting stored for
longer than the lifetime of the declaration of the class (i.e., the "stored
in database" example), I'll write code to deal with it, or to convert the
old versions to new versions.
--
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
|
 |
|  |
|  |
|
 |
|
 |
|  |
|
 |