Code specific to couples of classes

D

dl

I'll try to clarify the cryptic subject line.

Let's say I have a base class 'GeometricObject' with a virtual method
'double distance (const GeometricObject &) const'.

Among the derived classes I have eg 'Segment', representing a segment
of a line, and 'Arc', representing an arc of circle.

What is the right place to put the code for computing the distance
between a Segment and an Arc? It is not specific to the Segment nor to
the Arc, but to the coupling of the two objects.

Thank you,

Daniele
 
J

Jacek Dziedzic

dl said:
I'll try to clarify the cryptic subject line.

Let's say I have a base class 'GeometricObject' with a virtual method
'double distance (const GeometricObject &) const'.

Among the derived classes I have eg 'Segment', representing a segment
of a line, and 'Arc', representing an arc of circle.

What is the right place to put the code for computing the distance
between a Segment and an Arc? It is not specific to the Segment nor to
the Arc, but to the coupling of the two objects.

How about a global function, perhaps contained in the same
namespace your classes are in.

HTH,
- J.
 
A

Alan Johnson

dl said:
Thank you.

Yes it works. But is it "the right place"?

Absolutely.

If you want some authoritative word on that try Item 23 of Scott Meyer's
Effective C++: "Prefer non-member non-friend functions to member functions."
 
D

dl

Absolutely.

If you want some authoritative word on that try Item 23 of Scott Meyer's
Effective C++: "Prefer non-member non-friend functions to member functions."

Thank you.

I don't know this book. I will get a copy.
 
G

Grizlyk

Alan said:
Absolutely.

If you want some authoritative word on that try Item 23 of Scott Meyer's
Effective C++: "Prefer non-member non-friend functions to member
functions."

I do not know what Scott Meyer meant, but think, it is not general rule.
Member function can have encapsulated data so member function can be better
than plain function.

OP note, that the function "distance" can be shared between several classes,
else more than one copy of code must be at least maintained.

With "copy of code" in fact we are speaking about implementation of the
function "distance". So we need to have one implementation of the function
for all possible declarations of the function, in other words we need
abstract function declaration (interface) to be separated from the function
implementation.

In our case we have interface completely separated from its implementation.
In general case we can have only part of implementation of the interface
shared between several interfaces.

Let we will make the shared implementation as plain function. The plane
function (even placed into own namespace) does not allow us
- to use inheritance as design way to make new improved version of the plain
function,
- to have runtime template with the help of virtual function,
- to hide some data with the function
- and so on.

There are two design patterns existing to help to implement similar
separations: "bridge" and "strategy". The "strategy" can be useful design
pattern in our case.

Also free-standing class with inline members can be good solution instead of
"plane function placed into own namespace". There are no overhead here.

You also can declare inline member function in each class (if you want) to
compare with other class and make forward to its implementation. Consider
the following example:

-- cut here --

namespace Ndecl
{

class type{};

//
template<class First, class Second>
class Dist_implementation
{
public:
inline type distance( const First&, const Second& );
inline type distance( const Second&, const First& );
};

//
template<class First, class Second>
inline type distance( const First& f, const Second& s )
{
Dist_implementation<First,Second> di;
di.distance(f,s);
}

template<class First, class Second>
inline type distance( const Second& s, const First& f )
{
Dist_implementation<First,Second> di;
di.distance(s,f);
}

//namespace Ndecl
}

/*

//*******************************
-= C++ limitation =-
"define" does not exist as template parameter,
so can not do like this:

//declaration
template<define Dist_implementation>
class GeometricObject
{
protected:
template<class First, class Second>
type distance(const Second& obj)
{

//with "define" we avoid here
//"Dist_implementation is not template" problem
Dist_implementation<First,Second> di;

return
di.distance
(
static_cast<const First&>(*this),
obj
);
}
};

//inheritance
template<define Dist_implementation>
class Segment:
public GeometricObject<Dist_implementation>
{
typedef GeometricObject<Dist_implementation> Tparent;
using Tparent::distance;

public:
template<class Second>
type distance(const Second& obj)
{
return
//-= probably g++ limitation =- : Tparent::distance<Segment,Second>
static_cast<Tparent&>(*this).template
distance<Segment,Second>
(
obj
);
}
};

//instantiating
Segment<Ndecl::Dist_implementation> seg;

//implementation Ndecl::Dist_implementation

-=C++ limitation=-
can not point to itself in template parameter like this

template<>type
Dist_implementation<
class Segment said:
distance(
const Segment<Dist_implementation>&,
const Arc<Dist_implementation>&
)
{
}

so we are forced to use other namespace and
inherit only to make link to itself

namespace Nimpl
{
using Ndecl::Dist_implementation;

class GeometricObject:
public Ndecl::GeometricObject
<
Ndecl::Dist_implementation
{
....
};

class Segment:
public Ndecl::Segment
<
Ndecl::Dist_implementation
{
....
};

class Arc:
public Ndecl::Arc
<
Ndecl::Dist_implementation
{
....
};

//namespace Nimpl
}

// *******************************************

so in order to separate "Dist_implementation" and
"GeometricObject" we must use other namespace and
"using" directive or must use "#define" directive
of preprocessor

*/

namespace Ndecl2
{
using Ndecl::Dist_implementation;
using Ndecl::type;

//
class GeometricObject
{
//can not declare here and it is correct
//Dist_implementation di;

protected:
template<class First, class Second>
type distance(const Second& obj)
{
//can be worse than global data due to ctor/dtor calls
Dist_implementation<First,Second> di;

return
di.distance(
static_cast<const First&>(*this),
obj
);
}
};

// ***
//
class Segment:
public GeometricObject
{
public:
template<class Second>
type distance(const Second& obj)
{
return
GeometricObject::distance<Segment,Second>
(
obj
);
}
};

//
class Arc:
public GeometricObject
{
public:
template<class Second>
type distance(const Second& obj)
{
return
GeometricObject::distance<Arc,Second>
(
obj
);
}
};

//namespace Ndecl2
}

//specialization
namespace Ndecl
{
using Ndecl2::Arc;
using Ndecl2::Segment;

//
template<>type
Dist_implementation<Arc,Segment>::
distance
(
const Arc&,
const Segment&
)
{
//your "distance" for Arc& and Segment& here
return type();
}

//
template<>type
Dist_implementation<Arc,Segment>::
distance
(
const Segment& s,
const Arc& a
)
{
return distance(a,s);
}

/*
-=C++ limitation=-
can not declare comutative template parameters like this

template<>
class Dist_implementation<Segment,Arc>
is alias Dist_implementation<Arc,Segment>;

It is maybe correct, I think C++ must not have complicated
template design stuff integrated to itself, but C++ must
have only integrated bidirectional bridge to the external
stuffs like this.

It is due to the possible integrated design stuff can turn C++
into "langauge of exceptions from self rules", but
simultaneosly the integrated stuff always will be limited
by ordinary C++ syntax and that is why can be unsuitable
for concrete application domain.

*/

template<>
class Dist_implementation<Segment,Arc>
{
Dist_implementation<Arc,Segment> di;

public:
type distance( const Segment& s, const Arc& a )
{
return di.distance(s,a);
}

type distance( const Arc& a, const Segment& s )
{
return di.distance(a,s);
}

};

//namespace Ndecl
}


// *********************************************
//
int main()
{
using namespace Ndecl2;

Segment seg;
Arc arc;

//the following are equal

seg.distance<Arc>(arc);
arc.distance<Segment>(seg);

Ndecl::Dist_implementation<Arc,Segment> di;
di.distance(arc,seg);

Ndecl::distance<Arc,Segment>(arc,seg);
}

-- cut here --


--
Maksim A. Polyanin
http://grizlyk1.narod.ru/cpp_new

"In thi world of fairy tales rolls are liked olso"
/Gnume/
 
D

dave_mikesell

Let's say I have a base class 'GeometricObject' with a virtual method
'double distance (const GeometricObject &) const'.

Among the derived classes I have eg 'Segment', representing a segment
of a line, and 'Arc', representing an arc of circle.

What is the right place to put the code for computing the distance
between a Segment and an Arc? It is not specific to the Segment nor to
the Arc, but to the coupling of the two objects.

Seems that to compute the distance between any two GeometricObjects
you would need to know the center point of each, which could be stored
and fetched at the base class level. If that's the case, the distance
function could be defined there as well.
 
D

dl

Seems that to compute the distance between any two GeometricObjects
you would need to know the center point of each, which could be stored
and fetched at the base class level. If that's the case, the distance
function could be defined there as well.

Not so if for distance you mean 'the shortest distance between any
point on the arc and any point on the segment'. Anyway this is
geometry, not c++. There are plenty of different examples, not
geometric, where you need to write code specific to the coupling of
class A with class B.

Anyway I think this is a limitation of the OO approach, not of c++.
Or maybe 'limitation' is too strong; just something not fitting well
in OO.

Thank you all,

Daniele
 
G

Grizlyk

Gavin said:


1.
He has written: "It's important that we're trying to choose between member
functions and non-friend non-member functions".

As i can understand "non-friend non-member function" is a function, that can
use only public interface of the class. It is unsuitable rule for our
example, as i can guess.


2.
He has written: "... Wombat clients often need a number of convenience
functions related to eating, sleeping, and breeding. Such convenience
functions are by definition _not strictly necessary_."

From the first look on the page i think, that Scott Meyer can mix in
reader's mind interface and implementation.

Somebody could think, that if implementation of a member can be separated
from class, than interface _must_ be separated also (member removed as _not
strictly necessary_). It is not true in general.

For ordinary OO design we are jumping from interface, without taking
implementation into account. We must not try to find effective
implementation befor interface has been logically completed.

Probably he is speaking about the case, when we can define logically
completed interface in free manner, we have no fixed requirements.

Is GeometricObject::distance<> member strictly necessary or not? Not easy to
answer looking from GeometricObject::distance<> side.

Scott Meyer advice to remove the member (as _not strictly necessary_) if
"non-friend non-member function" exist. I can not agree or disagree with the
his rule, because have no enough expirence and other reliable information
for interface without fixed requirements.

Interfaces can be predefined. Concrete members of interfaces of high level
classes strictly defined by application domain, we can not reduce the
interface for the sake of "more effective implementation". The interfaces of
low level classes (as list, vector) often defined by reusage and other
purposes, so we can not reduce it in free manner also.

Interface is declaration, so it gives no code and we must not try to reduce
logicaly correct interface only in order to get its implementation more
effective. There are some stuffs, as design patterns for example, with the
help of which we can get optimal separated implementation for most existed
in nature interfaces.

For most, but it is not easy. From my point of view it is always hard to
make interfaces in plactical work, it is always iterational process, often
with blind branches of development. I do not know any universal and fast
method of design, but I will not remove members from logically completed
interface of class to namespace scope, as maybe he advice based on "existing
non-friend non-member function" criterion.

By the way, using the accident, I can say about weak point of C++ again - it
is hard to work with already defined classes in C++, due to C++ has no
standard bidirectional bridge between external OO desing tools (as Smalltalk
workplace) and C++ source code. We can not add tools, can not switch from
one to other as we need. I will write (want to write) about the bridge (the
bridge can be developed with the help of extra information about "class
items") on my page maybe during nearest months.


3.
In general any the "non-friend non-member function" is more independent than
any other function using protected data of the class, but more independent
always only due to class interface, not due to place of the function
declaration.

So it does not matter for the function where the kind of function has
declared: as member or as non-member and in my previous example i have shown
that member can be at least not worse.

He has written: "it becomes clear that a class with n member functions is
more encapsulated than a class with n+1 member functions". Maybe he want to
say, that small interface is better than large one.

A class with interface separated into small parts can seem easyer than the
same class with large interface, but last can seem more "solid", joining
members together for search and work with the class.

In my example there is extra link between member (implemeted as forwarding
function) and its real implementation placed outside of class. But in
program the link always will exist at the point of the non-member function
call. It is impossible to hide the link with the help of non-member.

We can use parameter of the concrete implementation as parameter of
templated member, so can use multiple implementations for one class (as with
non-member):

//
class GeometricObject
{
protected:
template<
class First,
class Second,
class Dist_implementation=
Ndecl::Dist_implementation said:
type distance(const Second& obj)
{
Dist_implementation di;

return
di.distance(
static_cast<const First&>(*this),
obj
);
}
};

but it is often better to link concrete implementation with the class
GeometricObject (as i have done befor), rather than with its member
"distance".


--
Maksim A. Polyanin
http://grizlyk1.narod.ru/cpp_new

"In thi world of fairy tales rolls are liked olso"
/Gnume/
 
G

Gavin Deane

1.
He has written: "It's important that we're trying to choose between member
functions and non-friend non-member functions".

As i can understand "non-friend non-member function" is a function, that can
use only public interface of the class.

No. A non-member non-friend *is part of* the interface. It operates on
objects through the public member functions, which are another part of
the interface. The whole point of that article is that it is a mistake
to think that public member functions are the only thing that make up
a class's interface.

Read the first paragraph under the heading Interfaces and Packaging.

He has written: "it becomes clear that a class with n member functions is
more encapsulated than a class with n+1 member functions". Maybe he want to
say, that small interface is better than large one.

No, that's missing the point. A class with n public member functions
plus 1 non-member non-friend function that works with those public
member functions has exactly the same size interface as a class with n
+1 public member functions. But the former is a more encapsulated
interface.

Gavin Deane
 
G

Grizlyk

Grizlyk said:
template<
class First,
class Second,
class Dist_implementation=

type distance(const Second& obj)

Surprise, surprise!
"default template arguments may not be used in function templates"

No one can predict the limitation, of course, becasue no reasons to enable
defaults for classes, but disable it for functions. You can make class here:

//declaration
template<
class First,
class Second,
class Dist_implementation=
Ndecl::Dist_implementation said:
struct Tdummy
{
type distance(const Second& obj){...
};

//usage
GeometricObject::Tdummy<First, Second> dummy;
dummy.distance(...

GeometricObject::
Tdummy<First, Second, New_Dist_implementation>
dummy;

dummy.distance(...

--
Maksim A. Polyanin
http://grizlyk1.narod.ru/cpp_new

"In thi world of fairy tales rolls are liked olso"
/Gnume/
 
G

Grizlyk

Gavin said:
No. A non-member non-friend *is part of* the interface. It operates on
objects through the public member functions, which are another part of
the interface. The whole point of that article is that it is a mistake
to think that public member functions are the only thing that make up
a class's interface.

It is standard OO convention "to thing that class's interface is only class
members". We can speak about namespace as a kind of opened interface, about
two classes, representing solid interface and so on, but there are other
things, another terms, not "class's interface".

--
Maksim A. Polyanin
http://grizlyk1.narod.ru/cpp_new

"In thi world of fairy tales rolls are liked olso"
/Gnume/
 
?

=?iso-8859-1?q?Erik_Wikstr=F6m?=

It is standard OO convention "to thing that class's interface is only class
members". We can speak about namespace as a kind of opened interface, about
two classes, representing solid interface and so on, but there are other
things, another terms, not "class's interface".

Well, my dictionary says that the verb "interface" means to interact.
So an interface of an object is the means with which we interact with
the object, be they member methods or not.

You can always be a purist and stick to some definition made by
someone sometime, but for the rest of the world a notation, such as
OO, changes over time (just as the meaning of words in natural
language) as new ways and uses are discovered. Personally I think it's
a good idea to include non-friend non-member functions in the
interface sine it makes a lot of classes more useful. Just look at
std::vector, it's a sequential container in which you can put/access/
remove thing and not much more. But if you add all the functionality
from <algorithms> it's suddenly a lot more useful.
 
G

Grizlyk

Erik said:
You can always be a purist and stick to some definition made by
someone sometime, but for the rest of the world a notation, such as
OO, changes over time (just as the meaning of words in natural
language) as new ways and uses are discovered.

No, there is not "purism" here. The term "interface of class" means exactly
"some members of class". It you will use term "interface of class" as
"interface of namespace" you should invent new term for "interface of
class". And what is reason to do it? No reasons.
Personally I think it's a good idea to include non-friend non-member
functions
in the interface sine it makes a lot of classes more useful.

Yes, you can do including non-member functions into interface, but into
"interface of namespace", not into "interface of class".

Scott Meyer could say, that class must not be used separatedly, only with
its special namespace:

namespace Nclass_name{
class Tclass_name
{
public:
// some members
// are belonging to "interface of class Tclass_name"
// are belonging to "interface of namespace Nclass_name"
// as Tclass_name::foo, Tclass_name::boo
void foo();
void boo();
};

// some non-members
// are not belonging to "interface of class Tclass_name"
// but are belonging to "interface of namespace Nclass_name"
void foo(Tclass_name&);
void boo(Tclass_name&);

//namespace Nclass_name
}

and could say, that "interface of namespace Nclass_name" is more flexible
than interface of class Tclass_name", if he think so.

--
Maksim A. Polyanin
http://grizlyk1.narod.ru/cpp_new

"In thi world of fairy tales rolls are liked olso"
/Gnume/
 
G

Gavin Deane

GavinDeanewrote:


It is standard OO convention "to thing that class's interface is only class
members".

So what? It's that very convention that the article questions (at
least in its applicability to C++). You can take issue with Scott
Meyers' arguments and decide for yourself whether you agree with his
conclusions (personally I find his arguments compelling) but it's not
reasonable to dismiss the entire article solely on the grounds that
it's questioning a convention in the first place. What's wrong with
questioning a convention? How else would you know whether it's
appropriate?

Gavin Deane
 
G

Grizlyk

Gavin said:
Nothing, for the name nothing, except he can find people do not understand
what he has said.

But for the idea, i am afraid (i am speaking about traditional "interface of
class" as "members of class"):

I do not like, that his advice looks like he digs into implementation
details in order to make interface, because it is bad idea. I think he could
say, that he try to change interface, after "interface of class" has been
logically completed, so he has no any other criterions to make the
interface, he can improve the interface in free manner.

For the case of "unspecified interface" i can not agree or disagree, because
do not know what we must do in the case.


--
Maksim A. Polyanin
http://grizlyk1.narod.ru/cpp_new

"In thi world of fairy tales rolls are liked olso"
/Gnume/
 
G

Gavin Deane

Nothing, for the name nothing, except he can find people do not understand
what he has said.

Well he openly acknowledges that he expects you to be surprised by his
headline. But he doesn't leave it as just a headline. There's a few
pages of justification that follows.
But for the idea, i am afraid (i am speaking about traditional "interface of
class" as "members of class"):

I first read Scott Meyers's article after reading this http://www.gotw.ca/gotw/084.htm
from Herb Sutter. It provides a more in-depth example of how your
traditional definition of a class's interface may not be the most
appropriate definition in C++, using a class we are all familiar with.

Gavin Deane
 
J

Jerry Coffin

[email protected] says... said:
No, there is not "purism" here.

That, at least, is true.
The term "interface of class" means exactly "some members of class".

That, OTOH, is not true. It never was, and being "pure" about it doesn't
change anything -- it's just plain wrong.
It you will use term "interface of class" as
"interface of namespace" you should invent new term for "interface of
class". And what is reason to do it? No reasons.

Actually, there are very good reasons for putting the entire interface
to a class in a single namespace. What's unreasonable is the belief that
the entire interface to a class must consist of member functions. This
belief simply lacks factual basis.

[ ... ]
Yes, you can do including non-member functions into interface, but into
"interface of namespace", not into "interface of class".

Wrong! The interface to a class can consist partly or even entirely of
non-member functions. What makes you think otherwise?
 
G

Grizlyk

Gavin said:
I first read Scott Meyers's article after reading this
http://www.gotw.ca/gotw/084.htm
from Herb Sutter. It provides a more in-depth example of how your
traditional definition of a class's interface may not be the most
appropriate definition in C++, using a class we are all familiar with.

What do you think about my arguments? Do you agree that :
1.
and
2.
I do not like, that his advice looks like he digs into implementation
details in order to make interface, because it is bad idea.

or not?

--
Maksim A. Polyanin
http://grizlyk1.narod.ru/cpp_new

"In thi world of fairy tales rolls are liked olso"
/Gnume/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,019
Latest member
RoxannaSta

Latest Threads

Top