Need design advice

C

Carl Bevil

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
 
V

Victor Bazarov

Carl Bevil said:
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
 
C

Carl Bevil

After reading the rest, yes, you're unclear.

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

public:

here...

Yes, you're correct...
Shouldn't this be

class PointImplementation : public Point

;

Yes, correct again. :)
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.


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
 
I

Ivan Vecerina

Carl Bevil said:
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
 
P

Patrick Kowalzick

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
 
I

Ivan Vecerina

| 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
 

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

No members online now.

Forum statistics

Threads
474,146
Messages
2,570,832
Members
47,374
Latest member
anuragag27

Latest Threads

Top