473,699 Members | 2,276 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Creating an object that is read from an input stream.

I want to allow objects of my class to be read from an input stream. I am
having trouble with the implementation. Here are the different approaches I
have tried:

// Version 1.0 - Default constructors
class MyClass
{
Foo foo; // foo and bar require default constructors
Bar bar;
public:
// default constructor for MyClass
MyClass() // foo and bar are default initialised
{
}

std::istream &extract(std::i stream &is)
{
if (!(is >> foo))
return is;
if (!(is >> bar))
return is;
return is;
}
};

std::istream &operator>>(std ::istream &is, MyClass &object)
{
return is >> object;
}

// Version 2.0 - std::istream constructor argument, default constructors and
exceptions
class MyClass
{
Foo foo; // foo and bar require default constructors
Bar bar;
public:
MyClass(std::is tream &is) // foo and bar are default initialised
{
if (!(is >> foo))
throw MyException;
if (!(is >> bar))
throw MyException;
}
};

// Version 3.0 - Shared pointers
class MyClass
{
Foo foo;
Bar bar;
public:
MyClass(Foo foo_, Bar bar_) : foo(foo_), bar(bar_) { }
};

std::istream &operator>>(std ::istream &is, SharedPtr<MyCla ss> &object_ptr)
{
SharedPtr<Foo> foo_ptr;
if (!(is >> foo_ptr))
return is;
Foo foo = *foo_ptr;

SharedPtr<Bar> bar_ptr;
if (!(is >> bar_ptr))
return is;
Bar bar = *bar_ptr;

object_ptr = new MyClass(foo, bar);
return is;
}

// Version 4.0 - Static read methods and exceptions
class MyClass
{
Foo foo
Bar bar;
public:
MyClass(Foo foo_, Bar bar_) : foo(foo_), bar(bar_) { }

static MyClass read(std::istre am &is);
};

MyClass MyClass::read(s td::istream &is)
{
try {
Foo foo = Foo::read(is);
Bar bar = Bar::read(is);
return MyClass(foo, bar);
} catch (FooException e) {
throw MyException(e.g et_desc());
} catch (BarException e) {
throw MyException(e.g et_desc());
}
}

The third version is the only approach that does not involve default
constructors or exceptions. How else can I write my class to allow it's
objects to be read from a stream? Thanks.
Jul 22 '05 #1
21 2517
I wrote version 2.0 wrong. Here is what I really meant to write:

// Version 2.0 - std::istream constructor argument and exceptions
class MyClass
{
Foo foo;
Bar bar;
public:
MyClass(std::is tream &is) : foo(is), bar(is)
{
}
};

This makes version 4.0 redundant.
Jul 22 '05 #2
"Jason Heyes" <ge******@optus net.com.au> wrote in message
news:3f******** **************@ news.optusnet.c om.au...
I want to allow objects of my class to be read from an input stream. I am
having trouble with the implementation. Here are the different approaches I have tried:

// Version 1.0 - Default constructors
class MyClass
{
Foo foo; // foo and bar require default constructors
Bar bar;
public:
// default constructor for MyClass
MyClass() // foo and bar are default initialised
{
}

std::istream &extract(std::i stream &is)
{
if (!(is >> foo))
return is;
if (!(is >> bar))
return is;
return is;
}
};

std::istream &operator>>(std ::istream &is, MyClass &object)
{
return is >> object;
This function does an infinite recursive call. Did you mean
object.extract( is)?
}
Why not make this function a friend of the class, and let it do what the
class extract function does, and get rid of the extract function? Why take
two steps when you can take one?

// Version 2.0 - std::istream constructor argument, default constructors and exceptions
class MyClass
{
Foo foo; // foo and bar require default constructors
Bar bar;
public:
MyClass(std::is tream &is) // foo and bar are default initialised
{
if (!(is >> foo))
throw MyException;
if (!(is >> bar))
throw MyException;
}
};
With this, you can't read into an existing object.

// Version 3.0 - Shared pointers
class MyClass
{
Foo foo;
Bar bar;
public:
MyClass(Foo foo_, Bar bar_) : foo(foo_), bar(bar_) { }
};

std::istream &operator>>(std ::istream &is, SharedPtr<MyCla ss> &object_ptr)
{
SharedPtr<Foo> foo_ptr;
if (!(is >> foo_ptr))
return is;
Foo foo = *foo_ptr;

SharedPtr<Bar> bar_ptr;
if (!(is >> bar_ptr))
return is;
Bar bar = *bar_ptr;

object_ptr = new MyClass(foo, bar);
return is;
}
All this looks like a lot of trouble.

// Version 4.0 - Static read methods and exceptions
class MyClass
{
Foo foo
Bar bar;
public:
MyClass(Foo foo_, Bar bar_) : foo(foo_), bar(bar_) { }

static MyClass read(std::istre am &is);
};

MyClass MyClass::read(s td::istream &is)
{
try {
Foo foo = Foo::read(is);
Bar bar = Bar::read(is);
return MyClass(foo, bar);
} catch (FooException e) {
throw MyException(e.g et_desc());
} catch (BarException e) {
throw MyException(e.g et_desc());
}
}


Again, you can't read into an existing object.

Of all these, version 1 (suitably modified) looks the best to me, but maybe
you have special reasons for preferring another.

DW

Jul 22 '05 #3
"Jason Heyes" <ge******@optus net.com.au> wrote in message
news:3f******** **************@ news.optusnet.c om.au...
I want to allow objects of my class to be read from an input stream. I am
having trouble with the implementation. Here are the different approaches I have tried:

// Version 1.0 - Default constructors
class MyClass
{
Foo foo; // foo and bar require default constructors
Bar bar;
public:
// default constructor for MyClass
MyClass() // foo and bar are default initialised
{
}

std::istream &extract(std::i stream &is)
{
if (!(is >> foo))
return is;
if (!(is >> bar))
return is;
return is;
}
};

std::istream &operator>>(std ::istream &is, MyClass &object)
{
return is >> object;
}

// Version 2.0 - std::istream constructor argument, default constructors and exceptions
class MyClass
{
Foo foo; // foo and bar require default constructors
Bar bar;
public:
MyClass(std::is tream &is) // foo and bar are default initialised
{
if (!(is >> foo))
throw MyException;
if (!(is >> bar))
throw MyException;
}
};

// Version 3.0 - Shared pointers
class MyClass
{
Foo foo;
Bar bar;
public:
MyClass(Foo foo_, Bar bar_) : foo(foo_), bar(bar_) { }
};

std::istream &operator>>(std ::istream &is, SharedPtr<MyCla ss> &object_ptr)
{
SharedPtr<Foo> foo_ptr;
if (!(is >> foo_ptr))
return is;
Foo foo = *foo_ptr;

SharedPtr<Bar> bar_ptr;
if (!(is >> bar_ptr))
return is;
Bar bar = *bar_ptr;

object_ptr = new MyClass(foo, bar);
return is;
}

// Version 4.0 - Static read methods and exceptions
class MyClass
{
Foo foo
Bar bar;
public:
MyClass(Foo foo_, Bar bar_) : foo(foo_), bar(bar_) { }

static MyClass read(std::istre am &is);
};

MyClass MyClass::read(s td::istream &is)
{
try {
Foo foo = Foo::read(is);
Bar bar = Bar::read(is);
return MyClass(foo, bar);
} catch (FooException e) {
throw MyException(e.g et_desc());
} catch (BarException e) {
throw MyException(e.g et_desc());
}
}

The third version is the only approach that does not involve default
constructors or exceptions. How else can I write my class to allow it's
objects to be read from a stream? Thanks.


When I'm writing software I find myself constantly asking, "what the heck
does A have to do with B?" In your case, what does MyClass have to do with
streams? I understand that I don't really know what MyClass is, but I'll bet
it doesn't have anything to do with streams. Thus I would be reluctant to
introduce streams into a constructor or built in function.

The obvious way is to use what I think of (non-standard term) a "delayed
constructor":

class MyClass
{
private:
Foo m_foo;
Bar m_bar;
public:
MyClass() {}
void set(const Foo &i_foo, const Bar &i_bar) {m_foo = i_foo; m_bar =
i_bar;}
...
};

The process for initialization would then be to read a temporary foo and bar
using software not shown here and then call set. Of course this might be
prohibitively expensive if Foo and Bar are large objects. (But do some run
time studies before you give up! A good optimizing compiler can sometimes
really surprise you.) If copying is really too expensive there may be
alternatives. For instance, lets say Foo is a actually a potentially large
std::vector. Then I would have a member function like this:

template <typename ITER>
set_foo(ITER first, ITER last) {std::copy(firs t, last,
std::back_inser ter(m_foo);}

Again, no specific reference to a stream although trivially usable with a
stream using istream_iterato r<>.

If all this fails I would fall back to Version 1.0. That seems like a
practical solution which mixes the concepts of MyClass and streams but at
least gets the job done efficiently.

--
Cy
http://home.rochester.rr.com/cyhome/
Jul 22 '05 #4
"David White" <no@email.provi ded> wrote in message
news:Ps******** **********@nasa l.pacific.net.a u...
This function does an infinite recursive call. Did you mean
object.extract( is)?
Oops. Yes I meant to write

return object.extract( is);
Why not make this function a friend of the class, and let it do what the
class extract function does, and get rid of the extract function? Why take
two steps when you can take one?
My compiler has always had problems with friend functions that overload >>.
I have gotten into the habit of writing extractors.
With this, you can't read into an existing object.
You can use the copy constructor to implement operator>> like so:

std::istream &operator>>(std ::istream &is, MyClass &object)
{
object = MyClass(is);
return is;
}

You might want to catch the exceptions thrown by
MyClass::MyClas s(std::istream &), writing:

std::istream &operator>>(std ::istream &is, MyClass &object)
{
try {
object = MyClass(is);
} catch (MyException e) { }
return is;
}

This allows you to read into an object.
All this looks like a lot of trouble.

Of all these, version 1 (suitably modified) looks the best to me, but maybe you have special reasons for preferring another.


I would use version 1.0 all the time if I could gaurentee that every class
had a default constructor. Here is an idea that I had:

// rename MyClass to MyClass_base
class MyClass_base
{
Foo foo;
Bar bar;
public:
MyClass_base(Fo o foo_, Bar bar_) : foo(foo_), bar(bar_) { }

Foo method(Bar bar) { /* do something */ }

Foo const_method(Ba r bar) const { /* do something */ }
};

// use new class MyClass that has a MyClass_base pointer
class MyClass
{
SharedPtr<MyCla ss_base> base;
public:
MyClass() { } // base is initialised to a null pointer

// mirror every constructor in MyClass_base
MyClass(Foo foo, Bar bar) : base(foo, bar) { }

// mirror every method in MyClass_base
Foo method(Bar bar)
{
base.make_uniqu e(); // copy-on-write
return base->method(bar);
}

Foo const_method(Ba r bar) const
{
return base->const_method(b ar);
}
};

std::istream &operator>>(std ::istream &is, MyClass &object)
{
Foo foo;
if (!(is >> foo))
return is;

Bar bar;
if (!(is >> bar))
return is;

object = MyClass(foo, bar);
return is;
}

If only there was a way to make this generic. It is too much work to mirror
every method of MyClass. I would rather implement a default constructor and
use version 1.0.
Jul 22 '05 #5
Before going any further I think it's necessary to know exactly what you
want to do, i.e., what you have to begin with and what you want to end up
with. I hate even mentioning anything MS-related here, but a lot of what you
are saying reminds me of MFC serialization (to which the CArchive class is
central), which enables you to serialize objects of classes derived from
CObject. I believe it can also handle members that are pointers to
serialized objects, including multiple pointers to the same object.
Basically, it builds up the same object network that was previously saved.
Of course, it has its limitations (a huge one being that everything must be
derived from CObject), but if this sounds like what you are after then it's
worth looking at to get ideas. I haven't looked at MFC serialization since
MFC version 1 or 2. It might be very different now.

DW

Jul 22 '05 #6
"David White" <no@email.provi ded> wrote in message
news:OQ******** **********@nasa l.pacific.net.a u...
Before going any further I think it's necessary to know exactly what you
want to do, i.e., what you have to begin with and what you want to end up
with. I hate even mentioning anything MS-related here, but a lot of what you are saying reminds me of MFC serialization (to which the CArchive class is
central), which enables you to serialize objects of classes derived from
CObject. I believe it can also handle members that are pointers to
serialized objects, including multiple pointers to the same object.
Basically, it builds up the same object network that was previously saved.
Of course, it has its limitations (a huge one being that everything must be derived from CObject), but if this sounds like what you are after then it's worth looking at to get ideas. I haven't looked at MFC serialization since
MFC version 1 or 2. It might be very different now.

DW


I don't want full-fledged serialisation or MFC until I absolutely need it.
All I really want right now is to read and write objects that do not have
pointer-type members and do not necessarily have an accessible default
constructor. Let me try to give a simple example. I will use the mechanism
in version 2.0 that I wrote before (istreams and exceptions) to achieve
serialisation.

class Box
{
int left, right, up, down;

int read_int(std::i stream &is)
{
int num;
if (!(is >> num))
throw ReadException;
return num;
}

public:
Box(std::istrea m &is) : left(read_int(i s)), right(read_int( is)),
up(read_int(is) ), down(read_int(i s)) { }

std::ostream &save(std::ostr eam &os)
{ return os << left << right << up << down; }

int get_area() const { return (right - left) * (up - down); }
};

// program that inputs and outputs a box
// the area of the box is shown
int main()
{
Box *mybox_ptr;
try {
mybox_ptr = new Box(cin);
} catch (ReadException e) {
return 0;
}
Box mybox = *mybox_ptr;

cout << mybox << endl;
cout << mybox.get_area( ) << endl;
return 0;
}

The main function shows the difficulty. You can't write

Box box;
if (!(cin >> box))
return 0;

Instead you must call the istream constructor and catch ReadException. I
find this counterintuitiv e to C++ and annoying. Here is another design that
might be an improvement. It relies on a private default constructor, some
generic methods and a custom reference class.

template <class T>
std::istream &operator>>(std ::istream &is, SharedPtr<T> &ptr)
{
T object; // default constructor must be accessible
if (!(is >> object))
return is;
ptr = new T(object);
return is;
}

template <class T>
std::ostream &operator<<(std ::ostream &os, const SharedPtr<T> &ptr)
{
return os << *ptr;
}
class Box
{
int left, right, up, down;

// default constructor is private
Box() { }

public:
int get_area() const { return (right - left) * (up - down); }

std::istream &extract(std::i stream &is)
{ return is >> left >> right >> up >> down; }

std::ostream &insert(std::os tream &os)
{ return os << left << right << up << down; }

// grant the extractor access to default constructor
friend std::istream &operator>>(std ::istream &is, SharedPtr<Box> &box);
};

std::istream &operator>>(std ::istream &is, Box &box)
{ return box.extract(is) ; }

std::ostream &operator<<(std ::ostream &os, Box &box)
{ return box.insert(os); }
// box reference
class BoxRef
{
SharedPtr<Box> ptr;

public:
int get_area() const { return ptr->get_area(); }

std::istream &extract(std::i stream &is) { return is >> ptr; }
std::ostream &insert(std::os tream &os) const { return os << ptr; }
};

std::istream &operator>>(std ::istream &is, BoxRef &box)
{ return box.extract(is) ; }

std::ostream &operator<<(std ::ostream &os, const BoxRef &box)
{ return box.insert(os); }
// program that inputs and outputs a box
// the area of the box is shown
int main()
{
BoxRef mybox;
if (!(cin >> mybox))
return 0;

cout << mybox << endl;
cout << mybox.get_area( ) << endl;
return 0;
}
Sorry for all the code. What do you think? Is this a practical solution?
Jul 22 '05 #7
"Jason Heyes" <ge******@optus net.com.au> wrote in message
news:3f******** **************@ news.optusnet.c om.au...
"David White" <no@email.provi ded> wrote in message
news:OQ******** **********@nasa l.pacific.net.a u...
Before going any further I think it's necessary to know exactly what you
want to do, i.e., what you have to begin with and what you want to end up with. I hate even mentioning anything MS-related here, but a lot of what you
are saying reminds me of MFC serialization (to which the CArchive class is central), which enables you to serialize objects of classes derived from
CObject. I believe it can also handle members that are pointers to
serialized objects, including multiple pointers to the same object.
Basically, it builds up the same object network that was previously saved. Of course, it has its limitations (a huge one being that everything must

be
derived from CObject), but if this sounds like what you are after then

it's
worth looking at to get ideas. I haven't looked at MFC serialization since MFC version 1 or 2. It might be very different now.

DW


I don't want full-fledged serialisation or MFC until I absolutely need it.
All I really want right now is to read and write objects that do not have
pointer-type members and do not necessarily have an accessible default
constructor. Let me try to give a simple example. I will use the mechanism
in version 2.0 that I wrote before (istreams and exceptions) to achieve
serialisation.

class Box
{
int left, right, up, down;

int read_int(std::i stream &is)
{
int num;
if (!(is >> num))
throw ReadException;
return num;
}

public:
Box(std::istrea m &is) : left(read_int(i s)), right(read_int( is)),
up(read_int(is) ), down(read_int(i s)) { }

std::ostream &save(std::ostr eam &os)
{ return os << left << right << up << down; }

int get_area() const { return (right - left) * (up - down); }
};

// program that inputs and outputs a box
// the area of the box is shown
int main()
{
Box *mybox_ptr;
try {
mybox_ptr = new Box(cin);
} catch (ReadException e) {
return 0;
}
Box mybox = *mybox_ptr;

cout << mybox << endl;
cout << mybox.get_area( ) << endl;
return 0;
}

The main function shows the difficulty. You can't write

Box box;
if (!(cin >> box))
return 0;

Instead you must call the istream constructor and catch ReadException. I
find this counterintuitiv e to C++ and annoying. Here is another design

that might be an improvement. It relies on a private default constructor, some
generic methods and a custom reference class.

template <class T>
std::istream &operator>>(std ::istream &is, SharedPtr<T> &ptr)
{
T object; // default constructor must be accessible
if (!(is >> object))
return is;
ptr = new T(object);
return is;
}

template <class T>
std::ostream &operator<<(std ::ostream &os, const SharedPtr<T> &ptr)
{
return os << *ptr;
}
class Box
{
int left, right, up, down;

// default constructor is private
Box() { }

public:
int get_area() const { return (right - left) * (up - down); }

std::istream &extract(std::i stream &is)
{ return is >> left >> right >> up >> down; }

std::ostream &insert(std::os tream &os)
{ return os << left << right << up << down; }

// grant the extractor access to default constructor
friend std::istream &operator>>(std ::istream &is, SharedPtr<Box> &box); };

std::istream &operator>>(std ::istream &is, Box &box)
{ return box.extract(is) ; }

std::ostream &operator<<(std ::ostream &os, Box &box)
{ return box.insert(os); }
// box reference
class BoxRef
{
SharedPtr<Box> ptr;

public:
int get_area() const { return ptr->get_area(); }

std::istream &extract(std::i stream &is) { return is >> ptr; }
std::ostream &insert(std::os tream &os) const { return os << ptr; }
};

std::istream &operator>>(std ::istream &is, BoxRef &box)
{ return box.extract(is) ; }

std::ostream &operator<<(std ::ostream &os, const BoxRef &box)
{ return box.insert(os); }
// program that inputs and outputs a box
// the area of the box is shown
int main()
{
BoxRef mybox;
if (!(cin >> mybox))
return 0;

cout << mybox << endl;
cout << mybox.get_area( ) << endl;
return 0;
}
Sorry for all the code. What do you think? Is this a practical solution?


Jason-

This is what I meant in a previous post about not mixing up different
concepts. What does a Box have to do with streams? Nothing. Consider:

#include <iostream>
#include <fstream>

class Box
{
private:
int m_left, m_right, m_up, m_down;

public:
Box() {}

Box(int i_left, int i_right, int i_up, int i_down) :
m_left(i_left), m_right(i_right ), m_up(i_up), m_down(i_down) {}

int left() const {return m_left;}

int right() const {return m_right;}

int up() const {return m_up;}

int down() const {return m_down;}
};

std::istream &
operator >> (std::istream &istr, Box &b)
{
int i_left, i_right, i_up, i_down;
istr >> i_left >> i_right >> i_up >> i_down;
b = Box(i_left, i_right, i_up, i_down);
return istr;
}

int main()
{
Box b;
std::ifstream ifs("box_number s.txt");
ifs >> b;
if (!ifs)
{
std::cout << "oops\n";
return 1;
}
std::cout << b.left() << ' ' << b.right() << ' '
<< b.up() << ' ' << b.down() << '\n';
}

As you can see, in C++ it is easy to separate I/O streams from the objects
they work on. This is basically how std::complex works, for instance,
although in that case the class was templated and wide character streams had
to be implemented. Note that the error handling here is just the same as it
might be with any other stream operation -- the fact that it happens to be a
Box shouldn't require special treatment.

Cy
Jul 22 '05 #8
"Cy Edmunds" <ce******@spaml ess.rochester.r r.com> wrote in message
news:%W******** ********@twiste r.nyroc.rr.com. ..
This is what I meant in a previous post about not mixing up different
concepts. What does a Box have to do with streams? Nothing. Consider:

#include <iostream>
#include <fstream>

class Box
{
private:
int m_left, m_right, m_up, m_down;

public:
Box() {}

Box(int i_left, int i_right, int i_up, int i_down) :
m_left(i_left), m_right(i_right ), m_up(i_up), m_down(i_down) {}

int left() const {return m_left;}

int right() const {return m_right;}

int up() const {return m_up;}

int down() const {return m_down;}
};

std::istream &
operator >> (std::istream &istr, Box &b)
{
int i_left, i_right, i_up, i_down;
istr >> i_left >> i_right >> i_up >> i_down;
b = Box(i_left, i_right, i_up, i_down);
return istr;
}

int main()
{
Box b;
std::ifstream ifs("box_number s.txt");
ifs >> b;
if (!ifs)
{
std::cout << "oops\n";
return 1;
}
std::cout << b.left() << ' ' << b.right() << ' '
<< b.up() << ' ' << b.down() << '\n';
}

As you can see, in C++ it is easy to separate I/O streams from the objects
they work on. This is basically how std::complex works, for instance,
although in that case the class was templated and wide character streams had to be implemented. Note that the error handling here is just the same as it might be with any other stream operation -- the fact that it happens to be a Box shouldn't require special treatment.

Cy


The Box class has a default constructor that leaves it's members undefined.
Therefore the Box object created in main is not valid until

ifs >> b;

occurs. If anyone uses the Box object before that line, it will lead to
unexpected results.

I don't see the point of the other constructor. Why not write:
class Box
{
int left, right, up, down;
public:
Box() { }
friend std::istream &operator>>(std ::istream &is, Box &box);
};

std::istream &operator>>(std ::istream &is, Box &box)
{
return is >> box.left >> box.right >> box.up >> box.down;
}

This way you don't need the other constructor. You could also write:

class Box
{
int left, right, up, down;
public:
Box() { }

std::istream &extract(std::i stream &is)
{
return is >> left >> right >> up >> down;
}
};

std::istream &operator>>(std ::istream &is, Box &box)
{ return box.extract(is) ; }
The real problem as I see it is to get around the default constructor. You
can make the default constructor private but that still allows invalid
objects to exist. Here is what I was thinking:
class Box
{
int left, right, up, down;

Box(int left_, int right_, int up_, int down_) :
left(left_), right(right_), up(up_), down(down_)
{ }

public:
int get_area() const { return (right - left) * (up - down); }

// reads a new box
static Box *create(std::is tream &is);

std::istream &extract(std::i stream &is)
{
int left, right, up, down;
if (!(is >> left >> right >> up >> down))
return is; // this box is valid even when input fails
*this = Bar(left, right, up, down);
return is;
}

std::ostream &insert(std::os tream &os) const
{ return os << left << right << up << down; }
};

// reads a new box
Box *Box::create(st d::istream &is)
{
int left, right, up, down;
if (!(is >> left >> right >> up >> down))
return 0;
return new Box(left, right, up, down);
}

// reads into an old box
std::istream &operator>>(std ::istream &is, Box &box)
{ return box.extract(is) ; }

std::ostream &operator<<(std ::istream &os, const Box &box)
{ return box.insert(os); }

class BoxRef
{
SharedPtr<Box> ptr;

public:
int get_area() const { return ptr->get_area(); }

std::istream &extract(std::i stream &is)
{
if (ptr) {
ptr.make_unique ();
return is >> *ptr;
}

Box *new_ptr = Box::create(is) ;
if (!new_ptr)
return is;

ptr = new_ptr;
return is;
}

std::ostream &insert(std::os tream &os) const
{ return os << *ptr; }
};

std::istream &operator>>(std ::istream &is, BoxRef &ref)
{ return ref.extract(is) ; }

std::ostream &operator<<(std ::ostream &os, const BoxRef &ref)
{ return ref.insert(os); }

int main()
{
BoxRef mybox;
if (!(std::cin >> mybox))
return 1;
std::cout << mybox.get_area( ) << std::endl;
std::cout << mybox << std::endl;
return 0;
}
At no point in time does an invalid Box object exist. Instead we allow a
null reference to exist and then we read into it. What do you think?
Jul 22 '05 #9
"Jason Heyes" <ge******@optus net.com.au> wrote in message
news:3f******** **************@ news.optusnet.c om.au...
"Cy Edmunds" <ce******@spaml ess.rochester.r r.com> wrote in message
news:%W******** ********@twiste r.nyroc.rr.com. ..
This is what I meant in a previous post about not mixing up different
concepts. What does a Box have to do with streams? Nothing. Consider:

#include <iostream>
#include <fstream>

class Box
{
private:
int m_left, m_right, m_up, m_down;

public:
Box() {}

Box(int i_left, int i_right, int i_up, int i_down) :
m_left(i_left), m_right(i_right ), m_up(i_up), m_down(i_down) {}

int left() const {return m_left;}

int right() const {return m_right;}

int up() const {return m_up;}

int down() const {return m_down;}
};

std::istream &
operator >> (std::istream &istr, Box &b)
{
int i_left, i_right, i_up, i_down;
istr >> i_left >> i_right >> i_up >> i_down;
b = Box(i_left, i_right, i_up, i_down);
return istr;
}

int main()
{
Box b;
std::ifstream ifs("box_number s.txt");
ifs >> b;
if (!ifs)
{
std::cout << "oops\n";
return 1;
}
std::cout << b.left() << ' ' << b.right() << ' '
<< b.up() << ' ' << b.down() << '\n';
}

As you can see, in C++ it is easy to separate I/O streams from the objects
they work on. This is basically how std::complex works, for instance,
although in that case the class was templated and wide character streams had
to be implemented. Note that the error handling here is just the same as

it
might be with any other stream operation -- the fact that it happens to be

a
Box shouldn't require special treatment.

Cy


The Box class has a default constructor that leaves it's members undefined.
Therefore the Box object created in main is not valid until

ifs >> b;

occurs. If anyone uses the Box object before that line, it will lead to
unexpected results.


Well, the default constructor can put it into a valid state then.
I don't see the point of the other constructor.
It's there so you can construct a box with whatever values you want. Is that not useful?
Why not write:

class Box
{
int left, right, up, down;
public:
Box() { }
friend std::istream &operator>>(std ::istream &is, Box &box);
};

std::istream &operator>>(std ::istream &is, Box &box)
{
return is >> box.left >> box.right >> box.up >> box.down;
}

This way you don't need the other constructor.
Having it means that you don't even have to make the function a friend, so there is not a single
mention of std::istream cluttering up the Box class. In any case, surely such a constructor is
virtually mandatory for this class, just for general-purpose use without streams.
You could also write:

class Box
{
int left, right, up, down;
public:
Box() { }

std::istream &extract(std::i stream &is)
{
return is >> left >> right >> up >> down;
}
};

std::istream &operator>>(std ::istream &is, Box &box)
{ return box.extract(is) ; }
The real problem as I see it is to get around the default constructor. You
can make the default constructor private but that still allows invalid
objects to exist.
By 'invalid' do you mean uninitialized, or not initialized with the right values (if the default
constructor were to initialize the members to default values)? And why is an "invalid" object a
problem? You are going to make it "valid" in the very next statement after you create it. For
example, is there anything disastrous about either of these?

int value;
is >> value;

Or:

int value = 0;
is >> value;

So what if the object you are reading into isn't the right value for a moment?
Here is what I was thinking:
class Box
{
int left, right, up, down;

Box(int left_, int right_, int up_, int down_) :
left(left_), right(right_), up(up_), down(down_)
{ }

public:
int get_area() const { return (right - left) * (up - down); }

// reads a new box
static Box *create(std::is tream &is);

std::istream &extract(std::i stream &is)
{
int left, right, up, down;
if (!(is >> left >> right >> up >> down))
return is; // this box is valid even when input fails
*this = Bar(left, right, up, down);
return is;
}

std::ostream &insert(std::os tream &os) const
{ return os << left << right << up << down; }
};

// reads a new box
Box *Box::create(st d::istream &is)
{
int left, right, up, down;
if (!(is >> left >> right >> up >> down))
return 0;
return new Box(left, right, up, down);
}

// reads into an old box
std::istream &operator>>(std ::istream &is, Box &box)
{ return box.extract(is) ; }
As I said in my first post (if I remember correctly back that far), why not ditch the extract
function in the class and make this function a friend so it can set the members directly? That
way you don't clutter your Box class with stream stuff.
std::ostream &operator<<(std ::istream &os, const Box &box)
{ return box.insert(os); }

class BoxRef
{
SharedPtr<Box> ptr;

public:
int get_area() const { return ptr->get_area(); }

std::istream &extract(std::i stream &is)
{
if (ptr) {
ptr.make_unique ();
return is >> *ptr;
}

Box *new_ptr = Box::create(is) ;
if (!new_ptr)
return is;

ptr = new_ptr;
return is;
}

std::ostream &insert(std::os tream &os) const
{ return os << *ptr; }
};

std::istream &operator>>(std ::istream &is, BoxRef &ref)
{ return ref.extract(is) ; }

std::ostream &operator<<(std ::ostream &os, const BoxRef &ref)
{ return ref.insert(os); }

int main()
{
BoxRef mybox;
if (!(std::cin >> mybox))
return 1;
std::cout << mybox.get_area( ) << std::endl;
std::cout << mybox << std::endl;
return 0;
}
At no point in time does an invalid Box object exist. Instead we allow a
null reference to exist and then we read into it. What do you think?


I think it's extremely and unnecessarily complicated. Apart from the new Box option (which could
easily be added to Cy's version), what are the advantages of your version?

DW

Jul 22 '05 #10

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

Similar topics

7
1440
by: andrea | last post by:
I was thinking to code the huffman algorithm and trying to compress something with it, but I've got a problem. How can I represent for example a char with only 3 bits?? I had a look to the compression modules but I can't understand them much... Thank you very much Any good link would be appreciated of course :)
7
1642
by: nog | last post by:
What's the best approach to creating many objects of a class? I have in mind using something analogous to a table to hold the data - which is in a form similar to (char name, char address, date joinDate) - and being able to declare each row, one after another by looping. With about 30 instances to create, I feel there must be a better way than making an individual declaration for each one. TIA.
3
5893
by: gouki | last post by:
suppose i have a class: class X { int i; string out; } X::out = "object x";
37
3393
by: Jason Heyes | last post by:
A pythagorean triple is a triple <a,b,c> whose components are positive integers satisfying a*a + b*b = c*c. An example is <3,4,5> since 3*3 + 4*4 = 9 + 16 = 25 = 5*5. I want to write a function to extract pythagorean triples from an input stream. The input is formatted so that only the first two components <a,b> of a pythagorean triple are specified. The function signature will be: std::istream &operator>>(std::istream &is,...
4
2752
by: tshad | last post by:
I am trying to set up an Image authorization where you type in the value that is in a picture to log on to our site. I found a program that is supposed to do it, but it doesn't seem to work. It should put a blue and yellow box on the page with "This is a test" as part of the picture. But what I get is a broken Gif. The other problem is that I can't view the source???? The view source is disabled for this page. What causes this?
3
1672
by: tshad | last post by:
oThumbnail.Save is giving me an error: System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+. theFileName = New FileInfo(strFileName) fileOut = fileIn.Substring(0,fileIn.LastIndexOf(".")) & session("User").CompanyID & fileIn.Substring(fileIn.LastIndexOf(".")) pathFileOut = Server.MapPath("\Uploads") & "\" & fileIn.Substring(0,fileIn.LastIndexOf(".")) & "Temp" &
15
2997
by: waltbrad | last post by:
Hello. I'm studying the book "C++ Primer Plus" by Stephan Prata. In chapter 6 he gives an exercise that reads from a file. The list is thus: 4 Sam Stone 2000 Freida Flass 100500 Tammy Tubbs
2
2929
by: =?Utf-8?B?UGF1bA==?= | last post by:
I am using ASP.Net 2.0 and VB.Net (although C#is ok also). I want to create an object/method/function that will take a URL as an input parameter and then return all of the HTML in that page. I also want to return the HTTP header information (response object). Does anyone have an insight as to any code samples or .Net objects I would use to accomplish this?
63
3233
by: Bill Cunningham | last post by:
I don't think I can do this without some help or hints. Here is the code I have. #include <stdio.h> #include <stdlib.h> double input(double input) { int count=0,div=0; double mean=0,linput=0; FILE *fpr, *fpw;
0
8703
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, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9185
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, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
1
8935
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 Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
8893
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 protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
1
6540
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5879
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4389
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 last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
1
3069
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 we have to send another system
3
2015
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

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.