Thursday, December 11, 2014

Digest for comp.lang.c++@googlegroups.com - 15 updates in 2 topics

Victor Bazarov <v.bazarov@comcast.invalid>: Dec 11 12:16PM -0500

On 12/11/2014 11:33 AM, JiiPee wrote:
> I am creating these Point (x, y) and Size (width, height) classes.
> Because they are pretty much the same thing I would like to re-use the
> Point class.
 
In what way would you "re-use" it?
 
> I could use width and height?
 
> Is there any way to hide the width and height for point and x, y for Size?
> [..]
 
You could have Size class derive privately from Point and define the
'width' and 'height' in it as _references_ to the base class' members
'x' and 'y', respectively. Initialize the refs in the Size's c-tor.
 
V
--
I do not respond to top-posted replies, please don't ask
JiiPee <no@notvalid.com>: Dec 11 06:21PM

On 11/12/2014 17:16, Victor Bazarov wrote:
>> Because they are pretty much the same thing I would like to re-use the
>> Point class.
 
> In what way would you "re-use" it?
 
So that Size would somehow use Point's code. So when I say:
Size s = Size(2,3);
 
all this code is from Point class. So Size is just another name for
Point kind of. The only difference being that I want Size to have width
instead of x member.
 
> 'width' and 'height' in it as _references_ to the base class' members
> 'x' and 'y', respectively. Initialize the refs in the Size's c-tor.
 
> V
 
Ok, I actually tried this already but got problems with return values:
 
CSize operator + (const CSize& point) const
{
return Point2D<T>::operator + (point);
}
 
its asking that + operator. So I have to call the parent class. But the
return value is Point2D :). I have to scratch my head a bit now how to
do this.. :)
JiiPee <no@notvalid.com>: Dec 11 06:25PM

On 11/12/2014 18:21, JiiPee wrote:
 
> its asking that + operator. So I have to call the parent class. But
> the return value is Point2D :). I have to scratch my head a bit now
> how to do this.. :)
 
Any hint how to return the Point2D + operator return value as CSize
type? the compiler is complaining about this.
JiiPee <no@notvalid.com>: Dec 11 06:51PM

Ok, because it was complaining I arranged what it asked. So, I set it:
 
CSize operator + (const CSize& point) const
{
return static_cast<CSize>(Point2D<T>::operator + (point));
}
 
But now its complaining that copy constructor: CSize::CSize(Point2D&) is
missing. So I added it:
 
CSize(const Point2D<T>& point) : Point2D<T>(point)
{
 
}
 
and it works. Is this the way to do it? I hope its not calling copy
constructors now like 3 times though... I guess the compiler will sort
it out, hopefully.
Victor Bazarov <v.bazarov@comcast.invalid>: Dec 11 02:19PM -0500

On 12/11/2014 1:21 PM, JiiPee wrote:
 
> all this code is from Point class. So Size is just another name for
> Point kind of. The only difference being that I want Size to have width
> instead of x member. [..]
 
My partially rhetorical question was in an attempt to draw your
attention that a Size *is not* a Point. You will probably see it later
when you add some functionality to Point, which you don't want to see in
Size, and vice versa.
 
What you need is the common base class for those. Find a copy of James
Coplien's "Advanced C++" (IIRC it was published in 1991) and read it
carefully. A quick hint: don't try to substitute one class for another
if there is no true substitution between them.
 
Here is one example of what some consider a fault in the design. As is
often suggested, a point in N-dimensional space *is a* vector in that
space. However, when some linear algebra interface is developed, we
need to remember that affine transformations when applied to a point
produce a different result than those applied to a vector (the
translation should be ignored for the latter transform). So, I have
seen more than once
 
typedef vectorNd pointNd;
 
and then somewhere
 
...
pointNd xformAsPoint(const pointNd &) const;
along with
vectorNd xformAsVector(const vectorNd &) const;
 
So, what's wrong, you might ask. Here is what's wrong. First off, I
want those two types to be distinct and have two *overloaded* functions
'xform' ("transform"), one for *a point* and one for *a vector*.
Second, I want to never be able to pass a point where a vector is
expected (and vice versa), so not to make a mistake. IOW, I want to be
able to require a cast made when one is converted into (treated as) the
other.
 
Such requirements prevent mistakes that the compiler cannot catch.
 
So, in your case, I strongly recommend figuring out what is common
between a Point and a Size, extracting that common portion into another
class and using that class as a base for both Point and Size.
 
Constructors are now (in C++11, and later) inherited, so you should be
able to do what you *already* want, i.e. to share the constructor
between those two types (three types, actually). But don't rush into
implementation. Design them first. Correctly.
 
V
--
I do not respond to top-posted replies, please don't ask
JiiPee <no@notvalid.com>: Dec 11 07:33PM

On 11/12/2014 19:19, Victor Bazarov wrote:
> attention that a Size *is not* a Point. You will probably see it
> later when you add some functionality to Point, which you don't want
> to see in Size, and vice versa.
 
Yes, but at least currently all they functions are the same. But ye, in
the future they might divert. Although if one wants to keep them simple
they might never change.
 
 
> want to be able to require a cast made when one is converted
> into (treated as) the other.
 
> Such requirements prevent mistakes that the compiler cannot catch.
 
deep... have to think about that :)
 
 
> So, in your case, I strongly recommend figuring out what is common
> between a Point and a Size, extracting that common portion into
> another class and using that class as a base for both Point and Size.
 
Yes, I was already thinking the same. Agree... I had just one problem:
how to call that base class?? :) How would you call it? Yes this is
indeed a good solution.
 
> between those two types (three types, actually). But don't rush into
> implementation. Design them first. Correctly.
 
> V
 
Yes.
JiiPee <no@notvalid.com>: Dec 11 07:46PM

On 11/12/2014 19:33, JiiPee wrote:
 
> Yes, I was already thinking the same. Agree... I had just one problem:
> how to call that base class?? :) How would you call it? Yes this is
> indeed a good solution.
 
I guess just "Vector2D" would be a good name? The only problem is that
its not really a full on vector, its kind of limited vector. So maybe:
"SimpleVector2D"?
Paavo Helde <myfirstname@osa.pri.ee>: Dec 11 03:18PM -0600

>> On 12/11/2014 11:33 AM, JiiPee wrote:
>>> I am creating these Point (x, y) and Size (width, height) classes.
>>> Because they are pretty much the same thing I would like to re-use
the
 
> all this code is from Point class. So Size is just another name for
> Point kind of. The only difference being that I want Size to have width
> instead of x member.
 
If this were the only difference, then having different names for the
members would be just an obfuscation, introducing additional complexity
without any benefits. In this case it would be just better to rename the
class to PointOrSize or IntPair or something like that.
 
But in general you cannot avoid some code duplication. Avoiding code
duplication is a tool to achieve the goal (a better program), not the
goal itself. For example, you may notice that in your code there are many
long keywords like "return" repeated all over. Does it make sense to
 
#define R return
 
and use R instead to reduce code duplication? No, because this is not the
goal!
 
Cheers
Paavo
Bo Persson <bop@gmb.dk>: Dec 11 10:19PM +0100

On 2014-12-11 20:33, JiiPee wrote:
 
> Yes, but at least currently all they functions are the same. But ye, in
> the future they might divert. Although if one wants to keep them simple
> they might never change.
 
But you don't want to use the same class for all objects containing two
int's, do you? Otherwise you could use std::pair<int, int> from the
standard library.
 
Conceptually a point and a size are totally different, so should be
different types.
 
Trying for code reuse at this level is a serious case of premature
optimization. Instead, trust your compiler to sort this out for you!
 
 
Bo Persson
JiiPee <no@notvalid.com>: Dec 11 09:30PM

On 11/12/2014 21:19, Bo Persson wrote:
 
> But you don't want to use the same class for all objects containing
> two int's, do you? Otherwise you could use std::pair<int, int> from
> the standard library.
 
But the point has many functions. pair could not do that. Or you mean
putting pair inside a class?
 
 
> Conceptually a point and a size are totally different, so should be
> different types.
 
But I think they have a lot of similar functions. I already did that.
And I put 11 member functions. I dont think much more will come, but
maybe could create a common base class: PointSize.... or something. No,
its only for point and size and similar things, not all pairs
 
 
> Trying for code reuse at this level is a serious case of premature
> optimization. Instead, trust your compiler to sort this out for you!
 
I dont know... I just dont like writing same code twice :). I like the
trick using the same code. Unless you have a very good reason not to do it..
Here are the common things:
 
Point2D() = default;
Point2D(const T& pointXY);
Point2D(const T& pointX, const T& pointY);
Point2D(const Point2D& point) = default;
 
Point2D& operator = (const Point2D& point) = default;
 
Point2D& operator = (const T& pointXY);
Point2D operator + (const Point2D& point) const;
Point2D operator - (const Point2D& point) const;
Point2D operator + (const T& pointXY) const;
Point2D operator - (const T& pointXY) const;
Point2D operator * (const T& pointXY) const;
Point2D operator / (const T& pointXY) const;
Point2D operator - () const;
 
T x;
T y;
JiiPee <no@notvalid.com>: Dec 11 09:32PM

On 11/12/2014 21:18, Paavo Helde wrote:
> goal itself. For example, you may notice that in your code there are many
> long keywords like "return" repeated all over. Does it make sense to
 
> #define R return
 
:)
 
 
> and use R instead to reduce code duplication? No, because this is not the
> goal!
 
Its more readable
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Dec 11 05:06PM

On 11/12/2014 00:17, JiiPee wrote:
> But seems to me that most library implementators prefer 1) version, even
> Microsoft.
 
> So a poll: how many or you chose a) and how man b)??
 
What you present in b) is neither a getter or a setter and practically
has no difference to public member variables. Getter/setter would be:
 
int X() const { return x; }
void setX(int aX) { x = aX; }
 
/Flibble
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Dec 11 05:09PM

On 11/12/2014 00:17, JiiPee wrote:
> But seems to me that most library implementators prefer 1) version, even
> Microsoft.
 
> So a poll: how many or you chose a) and how man b)??
 
All member variables that contribute to a class's invariant should be
private modulo const member variables.
 
/Flibble
"Öö Tiib" <ootiib@hot.ee>: Dec 11 10:03AM -0800

On Thursday, 11 December 2014 10:09:28 UTC+2, JiiPee wrote:
> But lets say you want to print/log to a file also something when x==0,
> that would not be possible I guess? For that we would need the setX
> -function.
 
Indeed, if you want to write some run-time sanity checks or debug
tracing or logging for (at least some of) occasions when value of
member changes (or is read) then that sort of debugging is easiest to
do with accessor functions. I trust that is what Sutter had in mind.
 
> a matter of taste as well I guess. But if most of the professionals use
> in libs publics here, then I guess its good to follow them...they all
> have been thinking about this and did their desisions.
 
On general case neither public accessor member functions nor public
member data do make sense. Every mutable member is usually related
with rest of the data that is managed by class. There are always
dependencies. Exposing it results that the class has effectively
dropped responsibility to manage its data.
 
For example about such dependencies imagine that "std::vector" had ...
3 getters: 'get_start()', 'get_finish()', 'get_end_of_storage()'
and 3 setters: 'set_start(s)', 'set_finish(f)', 'set_end_of_storage(e)'.
That is it. What else you need? It is all data members it has.
Indeed, such "vector" would be worthless. The classes that have lot
of getters and setters tend to be as worthless as rule.
Jorgen Grahn <grahn+nntp@snipabacken.se>: Dec 11 08:19PM

On Thu, 2014-12-11, JiiPee wrote:
...
> So far all libraries I have checked (3-4) use public x and y. For
> example b2Vec2 in physics library Box2D also doing it. So seems like
> putting them public seems to be prefered among library makers
 
But we're not library makers. Most classes we write will only be used
by other code which is under our control, so it's easy for us to
change the interface when needed.
 
/Jorgen
 
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: