473,508 Members | 2,053 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Need design advice

I'm creating a library for internal use that relies on third-party code. I
don't want clients of this library to know anything about the third party
code, when compiling or running. Generally I've been achieving this by having
an abstract base class which defines an interface, and inheriting a concrete
class which defines the implementation. Clients of the library deal only with
the base class and request objects of that type from a factory function.

This works great in most cases. The problems set in when you have operator
overloading. For example, I might have a class which defines 2-dimensional
point on the screen. I want the user to be able to use operator+, operator==,
operator=, etc... on this class.

Scanning the archives of this newsgroup it seems the big problem comes into
play with operator=, although I still see problems with the other operators as
well.

Hopefully what I'm saying is clear; if not, please let me know. :-)

What I'm trying to do is analogous to:

class Point
{
virtual void function(int x) = 0;
}

class PointImplemenation
{
virtual void function(int x);
}

except with operators:

class Point
{
virtual Point operator+(const Point& pt) = 0;
}

class PointImplementation
{
virtual PointImplementation operator+(const PointImplementation& pt);
}

I don't think this is going to work well at all, and it looks like a recipe
for many headaches in the future.

Anyone have an elegant solution for this problem? I've thought of the
possibility of using a proxy object instead of direct inheritance, although
that's not so great either, IMO.

I'm open to any ideas. :-)

Thanks!

Carl

--
Remove "NOSPAM" to reply in e-mail

Jul 19 '05 #1
5 2201
"Carl Bevil" <ca**************@yahoo.com> wrote...
I'm creating a library for internal use that relies on third-party code. I don't want clients of this library to know anything about the third party
code, when compiling or running. Generally I've been achieving this by having an abstract base class which defines an interface, and inheriting a concrete class which defines the implementation. Clients of the library deal only with the base class and request objects of that type from a factory function.

This works great in most cases. The problems set in when you have operator overloading. For example, I might have a class which defines 2-dimensional point on the screen. I want the user to be able to use operator+, operator==, operator=, etc... on this class.

Scanning the archives of this newsgroup it seems the big problem comes into play with operator=, although I still see problems with the other operators as well.

Hopefully what I'm saying is clear; if not, please let me know. :-)
After reading the rest, yes, you're unclear.

What I'm trying to do is analogous to:

class Point
{
Perhaps you need

public:

here...
virtual void function(int x) = 0;
} ;

class PointImplemenation
Shouldn't this be

class PointImplementation : public Point
{
virtual void function(int x);
} ;
except with operators:

class Point
{
public:
virtual Point operator+(const Point& pt) = 0;
}

class PointImplementation
Again, you probably meant

class PointImplementation : public Point
{
virtual PointImplementation operator+(const PointImplementation& pt);
This will not be an overrider. The return values have to be
at least covariant, which means either pointers or references
to related classes. Also, _arguments_ have to be the same.
}

I don't think this is going to work well at all, and it looks like a recipe for many headaches in the future.
Yes, it's not going to work.
Anyone have an elegant solution for this problem? I've thought of the
possibility of using a proxy object instead of direct inheritance, although that's not so great either, IMO.
That's not such a bad idea. You might want to read about the Visitor
pattern.

Also, since you've mentioned operator=, explore the necessity to add
a "clone" member function.

I'm open to any ideas. :-)


Why do you need 'operator+' redefined in the implementation class?
Isn't it simply a sum of the coordinates? Aren't the coordinates
known at the base class level?

Anyway, overloading operators is not an easy task for polymorphic
classes. The simplest answer to "how" is "don't". The biggest
problem in this situation is the "slicing" that occurs when you
return an object of the base class. Unfortunately, there is no
cure for slicing. The best thing would be to clone a point and
then use += on it (which returns a reference to the same object):

struct Point {
virtual ~Point() {}
Point* clone() const = 0;
virtual Point& operator +=(Point const&) = 0;
};

struct ParticularPoint : Point {
Point* clone() const {
return new ParticularPoint(*this); // or whatever
}

Point& operator +=(Point const& cp) {
// add coordinates of 'cp' to *this
return *this;
}
};

int main() {
ParticularPoint p1, p2;
Point* pp = p.clone();
*pp += p2;

delete pp;
}

Victor
Jul 19 '05 #2
On 05/11/2003 "Victor Bazarov" <v.********@comAcast.net> wrote:
"Carl Bevil" <ca**************@yahoo.com> wrote...

Hopefully what I'm saying is clear; if not, please let me know. :-)
After reading the rest, yes, you're unclear.


Sorry; that's what I get for coding in a newsreader....

What I'm trying to do is analogous to:

class Point
{


Perhaps you need

public:

here...


Yes, you're correct...
virtual void function(int x) = 0;
}

;

class PointImplemenation


Shouldn't this be

class PointImplementation : public Point
{
virtual void function(int x);
}

;


Yes, correct again. :-)
Anyone have an elegant solution for this problem? I've thought of the
possibility of using a proxy object instead of direct inheritance,

although
that's not so great either, IMO.


That's not such a bad idea. You might want to read about the Visitor
pattern.


Will do.
Also, since you've mentioned operator=, explore the necessity to add
a "clone" member function.

I'm open to any ideas. :-)
Why do you need 'operator+' redefined in the implementation class?
Isn't it simply a sum of the coordinates? Aren't the coordinates
known at the base class level?


No, in this case they are not. I used the Point class as an example, but in
my situation, the data is held by the derived class and is proprietary to the
third-party system I mentioned. The base class is meant only to define an
interface and not hold any data or implement any functionality.
Anyway, overloading operators is not an easy task for polymorphic
classes. The simplest answer to "how" is "don't". The biggest
problem in this situation is the "slicing" that occurs when you
return an object of the base class. Unfortunately, there is no
cure for slicing. The best thing would be to clone a point and
then use += on it (which returns a reference to the same object):

struct Point {
virtual ~Point() {}
Point* clone() const = 0;
virtual Point& operator +=(Point const&) = 0;
};
Should clone be virtual?
struct ParticularPoint : Point {
Point* clone() const {
return new ParticularPoint(*this); // or whatever
}

Point& operator +=(Point const& cp) {
// add coordinates of 'cp' to *this
return *this;
}
};

int main() {
ParticularPoint p1, p2;
Point* pp = p.clone();
*pp += p2;

delete pp;
}


I'm hoping to hide the derived class completely from clients. Thinking about
it more, this would probably require clients to always have pointers to Point
objects. Something like this:

Point* pt = Point::Create();

... and Point::Create() would be a factory which would create a ParticularPoint.

I don't think I like this after all. Perhaps I will use some sort of
proxy-ish object (I'll have a look at the Visitor pattern you mention).

Thanks, this has helped. Sorry about the code mistakes; I need more coffee...

Carl

--
Remove "NOSPAM" to reply in e-mail

Jul 19 '05 #3
"Carl Bevil" <ca**************@yahoo.com> wrote in message
news:Za********************@speakeasy.net...
I'm creating a library for internal use that relies on third-party code. I don't want clients of this library to know anything about the third party
code, when compiling or running. Generally I've been achieving this by having an abstract base class which defines an interface, and inheriting a concrete class which defines the implementation. Clients of the library deal only with the base class and request objects of that type from a factory function.

This works great in most cases. The problems set in when you have operator overloading. For example, I might have a class which defines 2-dimensional point on the screen. I want the user to be able to use operator+, operator==, operator=, etc... on this class.

Hi Car,

The two main C++ approaches to completely hide the implementation details
of a class are:
- Abstract base classes (which what you have going for)
- The pimpl idiom ( a.k.a. the Cheshire cat, and several other names).
When you do not need polymorphic behavior, the second option
is usually to be preferred.

A google search should do, but the basic idea is:
//file: Point.h

class Point {
public:
Point(); // sample constructor...
~Point();
... all public member functions here ...

//next 2 functions must be either implemented or private.
Point(Point const& orig);
Point& operator=(Point const& orig);

private:
struct Impl;
Impl* pimpl_; // as a unique data member
};
//file: Point.cpp

struct Point::Impl {
... put all the data members here ...
... utility functions and constructor if needed ...
};

Point::Point()
: pimpl_( new Impl() )
{
...
}

Point::~Point()
{
delete pimpl_;
}
If your class constructors may throw any exceptions, you should
use a smart pointer (e.g. std::auto_ptr or boost::scoped_ptr)
instead of the naked Impl* data member. This should actually
be the preferred default choice, unless you also want to
avoid exposing a smart-pointer header to your users.
( what I tend to do is put any throwing/complex
operations in the constructor of Point::Impl,
which makes it ok to keep a naked pointer ).

Regards,
Ivan
--
http://ivan.vecerina.com
Jul 19 '05 #4
Hi Ivan and NG,
If your class constructors may throw any exceptions, you should
use a smart pointer (e.g. std::auto_ptr or boost::scoped_ptr)
instead of the naked Impl* data member. This should actually
be the preferred default choice, unless you also want to
avoid exposing a smart-pointer header to your users.


Do you mean here, that I avoid all the pifalls when I throw exceptions
inside classes with pointers (allocating memory) when I just use a smart
pointer? This in fact would be very nice :-).

Coming back to the topic.

Assume my object exists e.g. a Point "p", and I have to pass it to another
class "bar". I could do this via a proxy "bar_proxy". "bar" needs "p" for
quite a long time, and I could not be sure, that "p" still exists, when
"bar" needs it. What means, I need here a kind of "delayed copying", that in
case the Point "p" is destructed, the "bar_proxy" creates an instance.

For a huge amount of Data (1000dim Point ;-) ) I assume that the class Point
itself is smart enough to do this job, what means inside my "bar_proxy" I
just create another "Point pp = p".
For small data-types (double, int,....) I just create a copy as well.

But what shall I do for "medium-sized" data-types ?

struct bar_proxy {
const Point pp; // nice for built-in types and "smart huge types"
// const Point & pp; // nicer, but if p ist destroyed it messes up my
mem.

bar_proxy(const Point& p) : pp(p);
};

Anyway, is this really a proxy or how shall I call it?

Regards,
Patrick
Jul 19 '05 #5
"Patrick Kowalzick" <Pa***************@cern.ch> wrote in message
news:bo**********@sunnews.cern.ch...
| Do you mean here, that I avoid all the pifalls when I throw exceptions
| inside classes with pointers (allocating memory) when I just use a smart
| pointer? This in fact would be very nice :-).
Well, it can help with a lot of the issues. But smart pointers
themselves aren't always trivial to use either...

| Coming back to the topic.
|
| Assume my object exists e.g. a Point "p", and I have to pass it to another
| class "bar". I could do this via a proxy "bar_proxy". "bar" needs "p" for
| quite a long time, and I could not be sure, that "p" still exists, when
| "bar" needs it. What means, I need here a kind of "delayed copying", that
in
| case the Point "p" is destructed, the "bar_proxy" creates an instance.
[...]
There are a few possible approaches.
- first, avoid copies as much as possible by passing objects
by reference when possible.
- the objects themselves may choose to implement some form of
reference counting, with or without automated COW (copy-on-write).
There are many options really.
I would suggest studying Scott Meyer's "(More) Effective C++" books
regarding these topics. You may also read about std::string (which
currently is typically implemented without reference counting,
because it is complex and inefficient in multithreaded apps).
Also, consider using boost::shared_ptr (from www.boost.org)
as a smart pointer for the contained data.

What you really need to chose AFAICT is whether a type will
automatically share its data among object copies or not.
If not, the user is responsible to take care of lifetime issues.
If yes, you need to decide wheter this sharing will be
visible to the users or not (if not, COW needs to be implemented).

| For a huge amount of Data (1000dim Point ;-) ) I assume that the class
Point
| itself is smart enough to do this job, what means inside my "bar_proxy" I
| just create another "Point pp = p".
| For small data-types (double, int,....) I just create a copy as well.
|
| But what shall I do for "medium-sized" data-types ?

There is no general rule about which approach is better.
Testing and tuning is typically required -- as for all optimizations
attempts (avoiding data copies is an optimization...).
Regards,
Ivan
--
http://ivan.vecerina.com
Jul 19 '05 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
3303
by: Google Mike | last post by:
I want to prepare some training for some new employees regarding the topic of multithreaded socket services implemented in PHP. I've been able to implement this with my own design, but I'd like to...
9
2912
by: sk | last post by:
I have an applicaton in which I collect data for different parameters for a set of devices. The data are entered into a single table, each set of name, value pairs time-stamped and associated with...
4
2293
by: Marquisha | last post by:
If this is off-topic, please forgive me. But I thought this might be the perfect spot to get some advice about how to proceed with a project. Working on a Web site design for a nonprofit...
38
2817
by: lawrence | last post by:
I'm just now trying to give my site a character encoding of UTF-8. The site has been built in a hodge-podge way over the last 6 years. The validator tells me I've lots of characters that don't...
2
1633
by: andyjgw | last post by:
Hi I'm a bit new to the designing of custom web page controls and using them in the properties designer window - need a little advice on a concept here. I have two properties in my control -...
12
1667
by: johannblake | last post by:
First off, I am NOT a beginner. I have lots of experience developing professional web sites and am a professional software developer. Unfortunately I've been out of web site development for the...
23
2351
by: JoeC | last post by:
I am a self taught programmer and I have figured out most syntax but desigining my programs is a challenge. I realize that there are many ways to design a program but what are some good rules to...
18
2307
by: bsruth | last post by:
I tried for an hour to find some reference to concrete information on why this particular inheritance implementation is a bad idea, but couldn't. So I'm sorry if this has been answered before....
2
1797
by: marklawford | last post by:
I've been prototyping a reporting solution using XSLT and Java to transform a number of XML files into different formats (PDF and CSV mainly). The XML comes from a legacy system where a number of...
2
1610
by: tatata9999 | last post by:
Currently my site is not using CSS. The thing I want to do with it is, I think, it needs a face lift, otherwise, it may look boring, target audience seems very much into visual stuff, flushy,...
0
7225
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
7124
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
7326
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
7385
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
1
7046
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
7498
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
3195
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
1558
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated ...
1
766
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.