By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
446,341 Members | 1,396 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 446,341 IT Pros & Developers. It's quick & easy.

reading lines from file

P: n/a
The question is pretty simple, i have a file called "primes.txt" and
funny enough it contains alot of primes (one per line)
Besides that i have an empty vector:
vector<__int64> P(0);

How do i fill that with the contents of the file?
P.push_back(line 1);
P.push_back(line 2);
ect.

I have tryed different stuff with getline() but it just wont work.
Hope someone can help ASAP.

Regards
Zacariaz

Jan 29 '06 #1
Share this Question
Share on Google+
24 Replies


P: n/a
fe**********@hotmail.com wrote:
The question is pretty simple, i have a file called "primes.txt" and
funny enough it contains alot of primes (one per line)
Besides that i have an empty vector:
vector<__int64> P(0);

How do i fill that with the contents of the file?
P.push_back(line 1);
P.push_back(line 2);
ect.

I have tryed different stuff with getline() but it just wont work.
Hope someone can help ASAP.


The file provides a sequence of values, and you need to copy that
sequence into a vector:

ifstream input("primes.tx");
copy(istream_iterator<__int64>(input), istream_iterator<__int64>(),
back_inserter(p));

This is a fundamental idiom. Learn it.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jan 29 '06 #2

P: n/a
Im trying to learn, thats why im asking...
anyway thank for the answer...

Jan 29 '06 #3

P: n/a
fe**********@hotmail.com wrote:
Im trying to learn, thats why im asking...


Sorry, didn't mean that to sound like a put-down.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jan 29 '06 #4

P: n/a
fe**********@hotmail.com wrote:
The question is pretty simple, i have a file called "primes.txt" and
funny enough it contains alot of primes (one per line)
Besides that i have an empty vector:
vector<__int64> P(0);

How do i fill that with the contents of the file?
P.push_back(line 1);
P.push_back(line 2);
ect.

I have tryed different stuff with getline() but it just wont work.


What about:

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <fstream>

int main ( void ) {
std::fstream primes ( "primes.txt" );
std::vector< int > p_vect;
// read
std::copy( std::istream_iterator<int>( primes ),
std::istream_iterator<int>(),
std::back_inserter( p_vect ) );
// write
std::copy( p_vect.begin(), p_vect.end(),
std::ostream_iterator<int>( std::cout, "\n" ) );
}
Best

Kai-Uwe Bux
Jan 29 '06 #5

P: n/a
Np, i dont have a hard time with that kind of remark, but other might.
;-)

Jan 29 '06 #6

P: n/a
The first piece of code suplyed by Pete worked fine, heres the code so
you can correct me if i have made a bummer ;-)

#include <iostream>
#include <vector>
#include <math.h>
#include <fstream>
#include <iterator>

using namespace std;

vector<__int64> P(0); // an empty vector to cotain the primes.
int prime_pointer = 0; // is used to make sure that the same prime
// isnt writen to file more than once.

void find_prime() { // searches for primes.
int p_size = P.size();
bool loop;
int i;
for (__int64 test = P.back() + 2; test > 0 && p_size == P.size();
test += 2) {
for (loop = true, i = 0; P[i] < sqrt(test) && loop == true;
i++) {
if (test % P[i] == 0) {
loop = false;
}
else if (P[i+1] > sqrt(test)) {
loop = false;
P.push_back(test);
}
}
}
}

void print_primes() { // print the complete P vector on
screen.
for (int i = 0; i < P.size(); i++) {
cout << P[i] << endl;
}
}

void read_primes() { // read primes from file.
ifstream primes("primes.txt");
if (!primes && P.size() == 0) {
P.push_back(2);
P.push_back(3);
}
else if (P.size() == 0) {
copy(istream_iterator<__int64>(primes),
istream_iterator<__int64>(), back_inserter(P));
prime_pointer = P.size();
}
primes.close();
}

void write_primes() { // write primes to file.
ofstream primes("primes.txt",ios::out | ios::app);
for (int i = prime_pointer; i < P.size(); i++) {
primes << P[i] << endl;
}
primes.close();
prime_pointer = P.size();
}

int main() {
// some code.
}

By the way i have only been coding c++ for about a week, so cut me some
slack eh? ;-)

Jan 29 '06 #7

P: n/a
fe**********@hotmail.com wrote:
The first piece of code suplyed by Pete worked fine, heres the code so
you can correct me if i have made a bummer ;-)

#include <iostream>
#include <vector>
#include <math.h>
#include <cmath>
#include <fstream>
#include <iterator>

using namespace std;
Questionable practice: you will want to avoid this in header files.
Also: what you show below might give rise to a header.

vector<__int64> P(0); // an empty vector to cotain the primes.
For the sake of this group, I would suggest the use of standard types; in
this case: unsigned long

int prime_pointer = 0; // is used to make sure that the same prime
// isnt writen to file more than once.
Generally, I frown upon the use of global variables. Looks like this wants
to be a class PrimeNumberTable.

void find_prime() { // searches for primes.
int p_size = P.size();
bool loop;
int i;
for (__int64 test = P.back() + 2; test > 0 && p_size == P.size();
test += 2) {
Will this outer for loop ever loop? P.size() will increase but where is
p_size updated. If this for loop executes only once, why is it a loop?
for (loop = true, i = 0; P[i] < sqrt(test) && loop == true;
i++) {
if (test % P[i] == 0) {
loop = false;
}
else if (P[i+1] > sqrt(test)) {
loop = false;
P.push_back(test);
}
}
}
}
Are you sure find_primes() is a good name. Looks like you really want to add
primes to the table.

void print_primes() { // print the complete P vector on screen.
for (int i = 0; i < P.size(); i++) {
cout << P[i] << endl;
}
}

std::copy( P.begin(), P.end(),
std::ostream_iterator<unsigned long>(std::cout, "\n") );
void read_primes() { // read primes from file.
ifstream primes("primes.txt");
if (!primes && P.size() == 0) {
P.push_back(2);
P.push_back(3);
}
else if (P.size() == 0) {
copy(istream_iterator<__int64>(primes),
istream_iterator<__int64>(), back_inserter(P));
prime_pointer = P.size();
}
primes.close();
The stream closes automaticall upon destruction.
}

void write_primes() { // write primes to file.
ofstream primes("primes.txt",ios::out | ios::app);
for (int i = prime_pointer; i < P.size(); i++) {
primes << P[i] << endl;
}
primes.close();
Stream closes automatically.
prime_pointer = P.size();
}

int main() {
// some code.
}
As I said, it looks like this wants to be a data structure. Something like

class PrimeNumberTable {

std::string file_name;
std::vector< unsigned long > data;
unsigned long file_size;

void read_file ( void );
// fills data from file

void update_file ( void );
// flushes additionally computed primes to file.

public:

PrimeNumberTable ( std::string f_name = "primes.txt" );
// init file_name
// read_file()
// set file_size

~PrimeNumberTable ( void );
// update the file on disk and store all additionally computed
// primes.

void extend_table ( unsigned long new_max_entry );
// add all prime numbers <= new_max_entry

}; // PrimeNumberTable
By the way i have only been coding c++ for about a week, so cut me some
slack eh? ;-)


In this group, we give you just enough rope to hang you :-)

Ah, nevermind. One week you say? Impressive!
Best

Kai-Uwe Bux
Jan 29 '06 #8

P: n/a
Thank you for the very nice walktrough, allthough i am only just
started with the program you have given me something to think about ;-)

heres an update, however none of the above has been taken in to account
yet...

#include <iostream>
#include <vector>
#include <math.h>
#include <fstream>
#include <iterator>

using namespace std;

vector<__int64> P(0); // an empty vector to cotain the primes.
int prime_pointer = 0; // is used to make sure that the same prime
// isnt writen to file more than once.
bool quit = false;
void find_prime() { // searches for primes.
int p_size = P.size();
bool loop;
int i;
for (__int64 test = P.back() + 2; test > 0 && p_size == P.size();
test += 2) {
for (loop = true, i = 0; P[i] < sqrt(test) && loop == true;
i++) {
if (test % P[i] == 0) {
loop = false;
}
else if (P[i+1] > sqrt(test)) {
loop = false;
P.push_back(test);
}
}
}
}

void print_primes() { // print the complete P vector on
screen.
for (int i = 0; i < P.size(); i++) {
cout << P[i] << endl;
}
}

void read_primes() { // read primes from file.
ifstream primes("primes.txt");
if (!primes && P.size() == 0) {
P.push_back(2);
P.push_back(3);
}
else if (P.size() == 0) {
copy(istream_iterator<__int64>(primes),
istream_iterator<__int64>(), back_inserter(P));
prime_pointer = P.size();
}
primes.close();
}

void write_primes() { // write primes to file.
ofstream primes("primes.txt",ios::out | ios::app);
for (int i = prime_pointer; i < P.size(); i++) {
primes << P[i] << endl;
}
primes.close();
prime_pointer = P.size();
}

void choice() {
int choice;
int amount;
system("cls");
cout << " 1. Find primes | 2. Print primes | 3. Write primes to
file | 4. Quit |" << endl;
cout << endl;
cout << " Make a choice: ";
cin >> choice;
if (choice == 1) {
cout << " How many primes do you want to find?: ";
cin >> amount;
system("cls");
cout << "Searching for primes..." << endl;
for (int i = 0; i < amount; i++) {
find_prime();
}
cin.get();
cout << "Done! Press enter to continue." << endl;
cin.get();
}
else if (choice == 2) {
system("cls");
print_primes();
cin.get();
cout << "Done! Press enter to continue." << endl;
cin.get();
}
else if (choice == 3) {
system("cls");
cout << " Writing primes to file: primes.txt" << endl;
write_primes();
cin.get();
cout << "Done! Press enter to continue." << endl;
cin.get();
}
else if (choice == 4) {
quit = true;
}
else {
system("cls");
cin.get();
cout << "Invalid choice! Press enter to continue." << endl;
cin.get();
}
}

int main() {
read_primes();
while (quit == false) {
choice();
}
}

try it, its a great waste of time...

Jan 29 '06 #9

P: n/a
k, now i have had some time to look at the walktrough...

1.
#include <math.h> #include <cmath>

i don really know the header cmath and math.h seems to work fine, so i
will need and explanation on that.

2. using namespace std; Questionable practice: you will want to avoid this in header files.
Also: what you show below might give rise to a header.

i must admit that i don really understand what u mean.

3. vector<__int64> P(0); // an empty vector to cotain the primes. For the sake of this group, I would suggest the use of standard types;
in
this case: unsigned long

as for the __int64, im using it because i have some problems with my
compiler, unsigned and long doesnt seem to make any difference and im
yet to experience if __int64 does.
by the way im using Bloodshed dev-c++

4. int prime_pointer = 0; // is used to make sure that the same prime
// isnt writen to file more than once. Generally, I frown upon the use of global variables. Looks like this
wants
to be a class PrimeNumberTable.

i couldnt agree more, however im still very inexperienced and havent
yet had a chance to learn about classes, actually i started writing it
all in the main function and then converted it all into single
funtions.maybe ill do some class thingy in a while ;-)

5. void print_primes() { // print the complete P vector on screen.
for (int i = 0; i < P.size(); i++) {
cout << P[i] << endl;
}
} std::copy( P.begin(), P.end(),
std::ostream_iterator<unsigned long>(std::cout, "\n") );

this is yet another smart thing that i dont understand, but i guess it
will come in time. you cant learn it all at once ;)

6.
The stream closes automaticall upon destruction.

if you are sure about that, ofcourse they have to go.

7.
Are you sure find_primes() is a good name. Looks like you really want
to add
primes to the table.

well, it might not be a good name, but it work for me ;-)

8. for (__int64 test = P.back() + 2; test > 0 && p_size == P.size();
test += 2) {

Will this outer for loop ever loop? P.size() will increase but where is

p_size updated. If this for loop executes only once, why is it a loop?

the for loop might as well have been an if statement, u r right about
that, but i not quite finish with it, in time it will execute a fixed
number of loops.
the reason i need an if statement is that i dont want the program to do
some thing weird when it reaches value higer than the __int64 can
contain. in my experience ull get some weird negative number if thats
the case, and thats the reason for the test > 0 statement.

9.
Ah, nevermind. One week you say? Impressive!
while thank you ;-)
although i have done a little php before, i must admit im a little
proud of myself.
thats all i guess, thanks once again...

Jan 29 '06 #10

P: n/a
fe**********@hotmail.com wrote:
k, now i have had some time to look at the walktrough...

1.
#include <math.h> #include <cmath>

i don really know the header cmath and math.h seems to work fine, so i
will need and explanation on that.


For some standard headers there are two versions. In this case, you used
<math.h>. The header <cmath> provides the same functions, except that it
defines everything within the namespace std. That is usually a Good Thing
(tm).
2.
using namespace std; Questionable practice: you will want to avoid this in header files.
Also: what you show below might give rise to a header.

i must admit that i don really understand what u mean.


If you say

using namespace std;

in a header file (e.g., prime_numbers.h), then every file including that
header will also have all identifiers from std dumped into global
namespace. It is considered Bad Practice (tm) for a library designer to
make such a decision for the clients.
3.
vector<__int64> P(0); // an empty vector to cotain the primes. For the sake of this group, I would suggest the use of standard types;
in
this case: unsigned long

as for the __int64, im using it because i have some problems with my
compiler, unsigned and long doesnt seem to make any difference and im
yet to experience if __int64 does.
by the way im using Bloodshed dev-c++


__int64 does not exist on all platforms. On my machine, your program simply
does not compile. __int64 is *not* standard C++. Your compiler happens to
support it, mine does not. Portability is a plus. In this case, you ca
achieve it without loosing anything.
4.
int prime_pointer = 0; // is used to make sure that the same prime
// isnt writen to file more than once.

Generally, I frown upon the use of global variables. Looks like this
wants
to be a class PrimeNumberTable.

i couldnt agree more, however im still very inexperienced and havent
yet had a chance to learn about classes, actually i started writing it
all in the main function and then converted it all into single
funtions.maybe ill do some class thingy in a while ;-)

5.
void print_primes() { // print the complete P vector on screen.
for (int i = 0; i < P.size(); i++) {
cout << P[i] << endl;
}
}

std::copy( P.begin(), P.end(),
std::ostream_iterator<unsigned long>(std::cout, "\n") );

this is yet another smart thing that i dont understand, but i guess it
will come in time. you cant learn it all at once ;)


The standard library has some very nifty concepts that interact quite
nicely:

* containers
* iterators
* algorithms

Containers encapsulate and abstract data structures like doubly linked lists
or balanced trees into things like std::list< some_type > or std::map< key,
value >. Iterators provide a uniform abstraction for access and traversal
of those data structures. Finally algorithms usually work on ranges defined
by pairs of iterators. This way, iterators decouple algorithms from
containers.
Best

Kai-Uwe Bux
Jan 29 '06 #11

P: n/a
fe**********@hotmail.com wrote:
Thank you for the very nice walktrough, allthough i am only just
started with the program you have given me something to think about ;-)

heres an update, however none of the above has been taken in to account
yet...
[code snipped]
try it, its a great waste of time...


Ok, and here is some code for you to chew on. You may find it worthwhile
figuring out how it does what it does. In particular, notice how the class
PrimeNumberTable encapsulates the file and vector handling.

Here goes:
#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>
#include <iterator>
#include <string>
#include <cassert>

class PrimeNumberTable {

typedef std::vector< unsigned long > Table;

public:

typedef Table::size_type SizeType;

private:

std::string file_name;
Table data;
SizeType file_size;

// prevent copying
PrimeNumberTable ( PrimeNumberTable const & );
PrimeNumberTable & operator= ( PrimeNumberTable const & other );

bool ungarded_is_prime ( unsigned long candidate ) const {
assert( data.back() >= std::sqrt( candidate ) );
for ( SizeType index = 0;
data[index] < std::sqrt( candidate );
++ index ) {
if ( candidate % data[index] == 0 ) {
return ( false );
}
}
return ( true );
}

void extend_by_bound ( unsigned long max_entry ) {
unsigned long next_candidate = data.back() + 2;
while ( next_candidate <= max_entry ) {
if ( ungarded_is_prime( next_candidate ) ) {
data.push_back( next_candidate );
}
next_candidate += 2;
}
}

void extend_by_length ( SizeType length ) {
unsigned long next_candidate = data.back() + 2;
while ( data.size() < length ) {
if ( ungarded_is_prime( next_candidate ) ) {
data.push_back( next_candidate );
}
next_candidate += 2;
}
}

public:

PrimeNumberTable ( std::string name = "primes.txt" )
: file_name ( name )
{
std::ifstream primes ( file_name.c_str() );
std::copy( std::istream_iterator<unsigned long>( primes ),
std::istream_iterator<unsigned long>(),
std::back_inserter( data ) );
file_size = data.size();
if ( data.size() < 2 ) {
data.clear();
data.push_back( 2 );
data.push_back( 3 );
}
}

~PrimeNumberTable ( void ) {
std::ofstream primes ( file_name.c_str(),
std::ios::out | std::ios::app );
std::copy ( data.begin() + file_size, data.end(),
std::ostream_iterator<unsigned long>( primes, "\n" ) );
}

std::ostream & print ( std::ostream & o_str ) const {
std::copy ( data.begin(), data.end(),
std::ostream_iterator< unsigned long >( o_str, "\n" ) );
return( o_str );
}

bool is_prime ( unsigned long candidate ) {
extend_by_bound( std::sqrt( candidate ) );
return ( ungarded_is_prime( candidate ) );
}

unsigned long nth_prime ( SizeType index ) {
// math guys beware: indexing starts with 0
extend_by_length( index+1 );
return( data[index] );
}

SizeType num_primes_less_than ( unsigned long bound ) {
extend_by_bound( bound );
return( std::lower_bound( data.begin(), data.end(), bound )
- data.begin() );
}

}; // PrimeNumberTable

int main() {
PrimeNumberTable t;
PrimeNumberTable::SizeType from = t.num_primes_less_than( 100 );
PrimeNumberTable::SizeType to = t.num_primes_less_than( 1000 );
std::cout << "The primes in the range [100,1000) are:\n";
for ( PrimeNumberTable::SizeType index = from; index < to; ++ index ) {
std::cout << t.nth_prime( index ) << '\n';
}
}
If you look closely enough, you will find that most of this is actually
inspired by the code you provided. Most of it, I just reorganized.
Best

Kai-Uwe Bux
Jan 29 '06 #12

P: n/a
Kai-Uwe Bux wrote:
~PrimeNumberTable ( void ) {
std::ofstream primes ( file_name.c_str(),
std::ios::out | std::ios::app );
std::copy ( data.begin() + file_size, data.end(),
std::ostream_iterator<unsigned long>( primes, "\n" ) );
}


Destructors that can throw are evil. Is this destructor evil?

Luke

Jan 29 '06 #13

P: n/a
<fe**********@hotmail.com> wrote in message
news:11**********************@g44g2000cwa.googlegr oups.com...
k, now i have had some time to look at the walktrough...

1.
#include <math.h> #include <cmath>

i don really know the header cmath and math.h seems to work fine, so i
will need and explanation on that.


math.h is a C header. Most of the C headers can be included by cxxxxx xxxx
being the old name without .h. The main reason to use cxxxx instead of the
old headers is to respect namespace (afaik). So the old header <string.h>
would be <cstring> <math.h> would be <cmath>, etc...
2.
using namespace std; Questionable practice: you will want to avoid this in header files.
Also: what you show below might give rise to a header.

i must admit that i don really understand what u mean.


Well, using namespace std; totally defeats the purpose of having namespaces
in the first place. Namespaces were created to get over the problem of name
collision. You declare a function MyFunction() in your program, but some
header your include also happens to have a MyFunction() declared, you get
name collision. But, if the header you include is using the std namespace,
then there is no collision. Your function is in the unnamed namespace, the
one you included is in the std namespace. Your function is gotten to by
MyFunction(), the one you included is gotten to by std::MyFunction().

Now, you say using namespace std; now you just brought everything you
included into the unnamed namespace, and you can get name collision. I know
it's a pain to keep typing std:: but you get used to it real quick so that
if you actually do it you soon don't even notice.
3.
vector<__int64> P(0); // an empty vector to cotain the primes. For the sake of this group, I would suggest the use of standard types;
in
this case: unsigned long

as for the __int64, im using it because i have some problems with my
compiler, unsigned and long doesnt seem to make any difference and im
yet to experience if __int64 does.
by the way im using Bloodshed dev-c++


As for unsigned and long not seeming to make a difference, try a quick test
and see.
long num1 = 0 - 1;
unsigned long num2 = 0 - 1;
std::cout << num1 << " " << num2 << std::endl;

output for my compiler is:
-1 4294967295
isn't it similar for yours?
4.
int prime_pointer = 0; // is used to make sure that the same prime
// isnt writen to file more than once. Generally, I frown upon the use of global variables. Looks like this
wants
to be a class PrimeNumberTable.

i couldnt agree more, however im still very inexperienced and havent
yet had a chance to learn about classes, actually i started writing it
all in the main function and then converted it all into single
funtions.maybe ill do some class thingy in a while ;-)


Well, you can declare this variable in main and pass it to read_primes() and
choice()
5.
void print_primes() { // print the complete P vector on screen.
for (int i = 0; i < P.size(); i++) {
cout << P[i] << endl;
}
} std::copy( P.begin(), P.end(),
std::ostream_iterator<unsigned long>(std::cout, "\n") );

this is yet another smart thing that i dont understand, but i guess it
will come in time. you cant learn it all at once ;)


This is probabably the correct c++ way to do it, but I'd probably do it like
you did anyway.
6.
The stream closes automaticall upon destruction.

if you are sure about that, ofcourse they have to go.
Yes, the stream closes automatically upon destruction, but I also close my
streams when I'm done with them. I feel it's kinda like a documentation in
the code saying I ain't gonna be using this stream no more in the current
method/function.
7.
Are you sure find_primes() is a good name. Looks like you really want
to add
primes to the table.

well, it might not be a good name, but it work for me ;-)
Actually, read_primes loads the table. Find primes seems to search the
table, so it seems like a good name to me. Although I didn't look at the
function real close.
9.
Ah, nevermind. One week you say? Impressive!
while thank you ;-)
although i have done a little php before, i must admit im a little
proud of myself.


You should be proud.
Jan 29 '06 #14

P: n/a
On Sun, 29 Jan 2006 11:03:13 -0500, Kai-Uwe Bux <jk********@gmx.net>
wrote:
What about:

int main ( void ) {
std::fstream primes ( "primes.txt" );
No error handling when open fails.
std::vector< int > p_vect;
// read
std::copy( std::istream_iterator<int>( primes ),
std::istream_iterator<int>(),
std::back_inserter( p_vect ) );
No error handling. What happens when a read error occcurs or when an
invalid 'prime' (some text) is read from "primes.txt"?
What if the user wants to know the line of the error?
// write
std::copy( p_vect.begin(), p_vect.end(),
std::ostream_iterator<int>( std::cout, "\n" ) );
Always returns EXIT_SUCCESS}


Jan 29 '06 #15

P: n/a
Luke Meyers wrote:
Kai-Uwe Bux wrote:
~PrimeNumberTable ( void ) {
std::ofstream primes ( file_name.c_str(),
std::ios::out | std::ios::app );
std::copy ( data.begin() + file_size, data.end(),
std::ostream_iterator<unsigned long>( primes, "\n" ) );
}


Destructors that can throw are evil. Is this destructor evil?


Yeah, I guess it's evil.

Note, however, that the "intended use" of this class would be to have one
static object constructed before or at the begining of main() and
automatically destructed upon termination of the program. That means that I
would not foresee destruction of PrimeNumberTable objects in the process of
stack unwinding. Admittedly, the class does not enforce the "intended use";
but I think that enforcing guidelines for use by code is not a Good Thing.
Best

Kai-Uwe Bux
Jan 29 '06 #16

P: n/a
Luke Meyers wrote:

Destructors that can throw are evil. Is this destructor evil?


False premise, so any statement follows from it.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jan 29 '06 #17

P: n/a
oh my, didnt count on that much response...
anyway, i have alot to look at and think about now... :O

Jan 30 '06 #18

P: n/a
Pete Becker wrote:
Luke Meyers wrote:

Destructors that can throw are evil. Is this destructor evil?


False premise, so any statement follows from it.


Are you familiar with the problems involved with destructors that can
throw, and do you have an example of a case in which some benefit
exists that outweighs them?

Luke

Jan 30 '06 #19

P: n/a
Kai-Uwe Bux wrote:
Luke Meyers wrote:
Destructors that can throw are evil. Is this destructor evil?
Yeah, I guess it's evil.

Note, however, that the "intended use" of this class would be to have one
static object constructed before or at the begining of main() and
automatically destructed upon termination of the program. That means that I
would not foresee destruction of PrimeNumberTable objects in the process of
stack unwinding.


The pragmatically important rule that destructors must not throw is
founded in the principle that the destructor has a contract to fulfill,
and throwing exceptions is inconsistent with that contract. Hence, in
a destructor, logic that can throw is misplaced. It should be moved to
a context where such code logically belongs, somewhere the exception
can be handled, rather than sabotaging the object's destruction. So
while the pragmatic rule may be less convincing in cases where the
chance of exception seems remote, I think it's still valid to say that
the offending logic is in the wrong place, which is a design flaw. Or,
at least, not to be imitated (or proffered for imitation).
Admittedly, the class does not enforce the "intended use";
but I think that enforcing guidelines for use by code is not a Good Thing.


I find that statement confusing -- could you please clarify what you
mean by this?

Luke

Jan 30 '06 #20

P: n/a
Luke Meyers wrote:
Kai-Uwe Bux wrote:
Luke Meyers wrote:
> Destructors that can throw are evil. Is this destructor evil?
Yeah, I guess it's evil.

Note, however, that the "intended use" of this class would be to have one
static object constructed before or at the begining of main() and
automatically destructed upon termination of the program. That means that
I would not foresee destruction of PrimeNumberTable objects in the
process of stack unwinding.


The pragmatically important rule that destructors must not throw is
founded in the principle that the destructor has a contract to fulfill,
and throwing exceptions is inconsistent with that contract.


My understanding is that the pragmatic rule "destructors better not throw"
is founded in the language rule that if a destructor throws during stack
unwinding, the program is doomed.

In the case under discussion, there is no need to ever declare a
PrimeNumberTable object within a try {} block: one needs only one prime
number table and that can be static. If one sticks to this guideline for
using the class, the destructor will never be called during stack
unwinding.

The only reason to turn PrimeNumberTable into a class (instead of a
namespace populated with free standind functions) is that this way one can
have a constructor that allows to specify a file name.
Hence, in
a destructor, logic that can throw is misplaced. It should be moved to
a context where such code logically belongs, somewhere the exception
can be handled, rather than sabotaging the object's destruction.
Well, your point about the contract is well taken. However, consider again
the case under discussion: the file only serves as a cache for prime
numbers already computed. If the destructor does not succeed in writing
them, there is no real harm: the object still keeps its promise, which is
to serve prime numbers upon demand. In order to do so, it will just have to
recompute them the next time. (The class is designed so that there is no
need to consider the side effect on the file as part of the contract. In
fact, since C++ never makes any guarantee that some data is *successfully*
written to a file, I would rather not make such side effects part of any
contract.)
So while the pragmatic rule may be less convincing in cases where the
chance of exception seems remote, I think it's still valid to say that
the offending logic is in the wrong place, which is a design flaw. Or,
at least, not to be imitated (or proffered for imitation).


If you read my post, you will find that code was offered as something to be
understood and scrutinized not as something to be imitated. Accordingly, I
am very happy that we have this interesting exchange.

To keep it interesting: may I ask how you would amend the PrimeNumberTable
class? Suppose that one of the the design goals is to hide the handling of
the cache from the client code as much as possible.

Admittedly, the class does not enforce the "intended use";
but I think that enforcing guidelines for use by code is not a Good
Thing.


I find that statement confusing -- could you please clarify what you
mean by this?


This would take the discussion away from interesting technical points toward
general philosophical remarks. Let me just say that I sometimes come across
posts (not in this thread) where people seem to be obsessed with designing
their library so that the clients way of using the library are tied down in
some ways. Those people ask questions that begin with "how can I make sure
that nobody can ...". Oftentimes, this looks like design overkill to me.
However, this is not closely related to the destructor issue under
discussion.
Best

Kai-Uwe Bux

Jan 30 '06 #21

P: n/a
Luke Meyers wrote:
Kai-Uwe Bux wrote:
~PrimeNumberTable ( void ) {
std::ofstream primes ( file_name.c_str(),
std::ios::out | std::ios::app );
std::copy ( data.begin() + file_size, data.end(),
std::ostream_iterator<unsigned long>( primes, "\n" ) );
}


Destructors that can throw are evil. Is this destructor evil?


I just considered this question once more; and I am not sure if I see where
this might throw: as far as I recall, the standard IO operations do not
throw but set the failbit of the stream instead. So, maybe the answer to
your question is simply: no, this destructor is not evil.

Which of the two statements would throw what? and under which circumstances?
Best

Kai-Uwe Bux
Jan 30 '06 #22

P: n/a
Luke Meyers wrote:
Pete Becker wrote:
Luke Meyers wrote:
Destructors that can throw are evil. Is this destructor evil?

False premise, so any statement follows from it.

Are you familiar with the problems involved with destructors that can
throw,


Yes.
and do you have an example of a case in which some benefit
exists that outweighs them?


Not off the to of my head. But now you're arguing not that they "are
evil" but that they should be used when their benefits outweigh their
drawbacks.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jan 30 '06 #23

P: n/a
Pete Becker wrote:
Luke Meyers wrote:
Pete Becker wrote:
Luke Meyers wrote:

Destructors that can throw are evil. Is this destructor evil?
False premise, so any statement follows from it.

Are you familiar with the problems involved with destructors that can
throw,


Yes.
and do you have an example of a case in which some benefit
exists that outweighs them?


Not off the to of my head. But now you're arguing not that they "are
evil" but that they should be used when their benefits outweigh their
drawbacks.


No, I challenge you to come up with such a case because I have
reasonable assurance that it doesn't exist. If it doesn't, then what
exactly is your point? There is no good reason for destructors to
throw, and many good reasons to forbid them to do so.

Luke

Jan 30 '06 #24

P: n/a
Luke Meyers wrote:
Pete Becker wrote:
Luke Meyers wrote:
Pete Becker wrote:
Luke Meyers wrote:
>Destructors that can throw are evil. Is this destructor evil?
>

False premise, so any statement follows from it.
Are you familiar with the problems involved with destructors that can
throw,
Yes.

and do you have an example of a case in which some benefit
exists that outweighs them?


Not off the to of my head. But now you're arguing not that they "are
evil" but that they should be used when their benefits outweigh their
drawbacks.

No, I challenge you to come up with such a case because I have
reasonable assurance that it doesn't exist.


Nope. You're the one who raised cost-benefit analysis. If they're simply
"evil" then it doesn't apply, they shouldn't be used regardless of
whether there are circumstances where they would be appropriate.
If it doesn't, then what
exactly is your point? There is no good reason for destructors to
throw, and many good reasons to forbid them to do so.


Okay, propose it as a language change. In the meantime, rather than do
silly two-phase destruction when it doesn't amtter, I have no qualms
about writing destructors that throw exceptions when they can't do what
they're supposed to do.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jan 30 '06 #25

This discussion thread is closed

Replies have been disabled for this discussion.