Loading 3 Votes - +

Using C++ Streams to Load Fixed Width Fields

Introduction

Despite the continuing advances in the capabilities of computer languages, such as C++, Fortran remains the lingua franca of computing in physics. On the whole, I prefer the syntax and flexibility of C/C++, despite its potential for abuse.1 With the appropriate libraries,2 and some investment of time, C++ can be used to write code that is easier to understand than,3 and comparable in speed to,4 Fortran. However, despite the power and ease of use of the streams library in C++, it is difficult to read-in fixed width fields without modifying the streams’ behavior, unlike in Fortran.

Take, for example, this Fortran code:
bc.. READ’ ) LATTIC,NAT
p.
Which reads a 4 character string into LATTIC, skips 23 characters, and then reads in 3 characters converting them into an integer, NAT, from file 20. Ideally, in C++, I would like to write:
bc.. fin >> setwidth( 4 ) >> lattice >> ignore( 23 ) >> setwidth( 3 ) >> nat;
p.
where fin is an input file stream5 and setwidth(int) and ignore(int) are manipulators6 that alter the operation of the stream. Specifically, setwidth(int) operates by performing the equivalent to the following:

fin.width( 4 ); 
fin >> lattice;
p. 

Unfortunately, there are two problems with this set up. First, the manipulator ignore(int) does not exist, which is easily resolved, as I’ll demonstrate below. The second, and more serious problem, is that the fin.width(int)7 only works with character arrays and their equivalents.8 This requires a manipulator that interacts with the stream on a deeper level than ignore(int) will, while maintaining our ability to continue using the stream mechanism.

Towards that end, I am going to demonstrate how to build C++ stream manipulators that allow an input stream to function in a similar manner to the Fortran read mechanism. Along the way, I will show how to build two different types of manipulators that partially reproduce the Fortran style read: one that operates immediately on the input stream (ignore(int)), and one whose operation is delayed (setwidth(int)). Ideally, I would like these manipulators to be drop in replacements for any existing manipulators, like std::setw(int). But, first we need to understand how streams and manipulators function.

Streams and the Objects That Manipulate Them

The C++ streams library is one of the most successful pieces of the standard library. Its usefulness rests on two things: operator overloading9 and templating.10 Each instance of a shift operation, >> or <<, corresponds directly to a function call that returns the stream object. A general form of this operator may have the signature,11
bc.. template< class StrmType, class DataType >
StrmType& operator>>( StrmType&, DataType& )
p.
So,
bc.. fin >> lattice;
p.
is interpreted as:
bc.. operator>>( fin, lattice );
p.
by the compiler, and since operator&gt;&gt; returns fin, the code:
bc.. fin >> lattice >> nat;
p.
is interpreted as:
bc.. operator>>( operator>>( fin, lattice ), nat );
p.
So, to extend the streams library, in most cases, all we have to do is to overload the shift operators. It is through this means that manipulators function.

Manipulators work by operating on the stream object passed in through the shift operators. The simplest of manipulators are functions that take a stream object as their only parameter. As an example, consider the manipulator flush, which causes the output stream to write out the contents of its buffer to the output device, such as stdout or a file. An output stream can be made to flush its buffer by using either:
bc.. fout.flush();
p.
or:
bc.. fout >> flush;
p.
A possible definition of flush is simply:12
bc.. ostream& flush( ostream& os ) {
return os.flush();
}
p.
More complicated manipulators that take parameters, like ignore(int), require more than a single function declaration to work.

Living With Being Ignored

A manipulator must have a means of storing a parameter in order for it to be able to accept one. This is most easily accomplished by using a function that returns a simple object which stores the parameter and performs the operation you are looking for within an overloaded shift operation.13 For our case, the object would be defined:14
bc.. struct ignore {
int data;
ignore( int n ) : data( n ) {}
};
p.
and its overloaded shift operator would be defined:
bc.. template<class StrmType>
StrmType& operator>>( StrmType& is, ignore idata ) {
is.ignore( idata.data );
return is;
}
p.
which has been templated to allow for any possible input stream. Since I specified a constructor, we could leave it like this, and it can be used as follows:15
bc.. fin >> ignore( 4 ) >> lattice;
p.
Also, it will sometimes be necessary to ignore the rest of a line, or multiple lines, in our input.

To create these manipulators, we are going to use the same technique that we used for ignore manipulator above, and start by defining a structure to hold our data:
bc.. struct IgnoreLineData {
int data;
IgnoreLineData(int n) : data(n) {}
};
p.
In order to extract a line from the stream, we’ll use getline(stream&amp;, string&amp;) because it extracts the rest of a line from the input stream. The shift operator will look like:
bc.. template <class StrmType>
StrmType& operator>>(StrmType& is, IgnoreLineData ild) {
std::string tmp;
for(int i = 0; i < ild.data; i++)
{
std::getline(is,tmp);
}
return is;
}
p.
Now, to put our IgnoreLineData object into the stream we use this function:
bc.. inline IgnoreLineData ignoreLines(int n) {
return IgnoreLineData(n);
}

To extract the remainder of a single line, we can either write:
bc.. fin >> ignoreLines(1);
p.
or, we can create a second function to do this for us16
bc.. inline IgnoreLineData ignoreToEOL() {
return IgnoreLineData(1);
}
p.

Fixed Width Field: Facing the Facets

Before we can get to bypassing the standard library’s restriction on fin.width(int), we need to determine how this manipulator will function. Take for instance:
bc.. fin >> setwidth( 4 ) >> lattice;
p.
Unlike ignore(int), the action of setwidth (int) is delayed until lattice is placed into the stream17, so setwidth(int) will require a little more scaffolding to work.

We are are going to temporarily replace the stream with our own object, but to do this requires a two step process. First of all, the object that will perform the manipulation needs to contain both the width and a reference to the stream, so that the stream can be placed back where it belongs. So, it should look something like this:
bc.. template < class StrmType >
class FixedWidthField {
StrmType& strm;
int width;

public: FixedWidthField( StrmType& s, int w ) : strm( s ), width( w ) {} StrmType& operator>>( FieldType& field );

};
p.
Now, the operation that will swap FixedWidthField back out is simply:
bc.. template < class StrmType, class FieldType >
StrmType& operator>>( FieldType& field ) {
//nifty code that gets us the fixed width field
return strm;
}
p.
and since it is templated, it can accept any FieldType.18 I’ve included it as a member operator to eliminate any ambiguities in identifying the correct function.19 To get a FixedWidthField into the stream and have it be appropriately parameterized, we need an object that looks very similar to ignore:
bc.. struct setwidth {
int width;
setwidth(int n) : width(n) {}
};
p.
and, again, the magic happens in the overloaded shift operator:
bc.. template < class StrmType >
FixedWidthField< StrmType > operator>>( StrmType& strm, setwidth& sw ) {
return FixedWidthField< StrmType >( strm, sw.width );
}

Now all we have to do is fill in the rest of the other shift operator function. Among the options for filling out the operator&gt;&gt; function for FixedFieldWidth,20 the most appealing is to use the stream library’s mechanism directly.

The C++ stream mechanism is divided into four parts: the stream, the buffer, the locale, and facets. The stream provides the public facade hiding the complexities of i/o, which is handled by the stream buffer. The conversion process from a stream of bits to a usable value is handled by a locale, which is really nothing more than a collection of facets. It is the facets that perform all of the conversions. Since, we are concerned with converting character arrays into numbers, the facet we need to work with is num_get&lt; _CharT, _InIter &gt;.21 This facet accepts two template parameters: the character type (i.e., char), and an iterator type. The default implementation of num_get uses streambuf iterators, and, ideally, we could also utilize streambuf iterators to extract the character representation directly. But, to use an iterator, you have to provide a stop point, and the streambuf iterators do not provide a method for advancing the iterator without also changing the stream buffer’s state. Specifically, if you have two streambuf iterators, both initially pointing at the beginning of the buffer, and you use the operator++ to advance one of the iterators, the underlying streambuffer’s internal state is advanced, invalidating the other iterator. The solution is to create another num_get object with a different parameterization.

The alternative iterator we will use is a character pointer (char*), since loading data into a character array is the only way that we’re guaranteed to get the exact number of characters that we ask for.22 To facilitate that, we’ll first add some additional typedefs to our FixedWidthField class:
bc.. template < class StrmType >
class FixedWidthField {
//other code

public: //typedefs created for convenience typedef typename StrmType::char_type char_type; typedef typename std::basic_string< char_type > str_type; //kept for convenience typedef typename std::num_get< char_type, char_type* > ng_type;

}
p.


template &lt; class StrmType &gt;
class FixedWidthField {
    //other code
    
    public:
        //typedefs created for convenience
        typedef typename StrmType::char_type char_type;
        typedef typename std::basic_string&lt; char_type &gt; str_type;
        typedef typename str_type::const_iterator str_iter;
        typedef typename std::num_get&lt; char_type, typename str_type::const_iterator &gt; ng_type;
}

which will help simplify our code.23 Additionally, we would like our version of num_get to be available at need while only having to create it once. So, we’ll modify the FixedWidthField constructor to attach num_get to our stream’s locale object the first time a FixedWidthField is created, like so:

FixedWidthField( StrmType&amp; s, int w ) : strm(s), width(w) {
    //check it strm already has our version of num_get
    if( !std::has_facet&lt; ng_type &gt;( strm.getloc() ) ) {
        //if not create a locale with it
        std::locale loc( strm.getloc(), new ng_type );
        
        //and attach it to our stream
        strm.imbue( loc );
    }
}
p. 
Now, that we have @num_get@ available when we need it, let's use it.  
p(. First, we need a temporary repository for our field:

template &lt; class FieldType &gt;
StrmType&amp; operator&gt;&gt;( FieldType&amp; field ) {
	//allocate space from the heap for this
	//The width + 1 is because strm will add a \0 to the array
	char_type* val = new char_type[width+1];

and pointers pointing to the beginning and the end24 of the array:
bc..
char_type* begin = val;
char_type* end = val + width + 1;

which we’ll use to stip off the leading and trailing whitespace. But, first we must load our field into our array:
bc..
strm.get( val, width + 1, StrmType::traits_type::eof() ); // pull our value off the stream

Now, we can strip off the leading and trailing whitespace using pointer arithmetic:
bc..
//trim the surrounding whitespace
while ( std::isspace( *begin ) ) {begin++;}
while ( std::isspace( *(end-1) ) ) {end—;}

Then we save the state of the stream:
bc..
std::ios_base::iostate state = strm.rdstate();
bool eofset = strm.eof(); //get eof state, and save for later

with special attention paid towards the end-of-file state, as the get( ... ) function we will use from num_get will set the eof flag. As I said before, a locale is just a collection of facets, and to access a facet within a locale we use the templated function:
bc..
//this changes state as needed.
std::use_facet< ng_type >( strm.getloc() ).get( begin, end, strm, state, field);

accessing the get function directly from the returned value. To ensure that there are no memory leaks, we must return val to the heap:
bc..
//recover allocated space
delete val;

Finally, we must restore the stream state:
bc..
//the get operation sets the eof flag erroneously (at least to us), so
//if not set after read from stream, then remove from state
if( !eofset ) {
// ~ is the complement operator, e.g. ~1101 == 0010
//therefor this will be all ones except for the eofbit.
state &= ( ~std::ios_base::eofbit );
}

//install the new state; this will throw an exception if the state is not good and the exception flag has been set strm.setstate( state ); return strm;

}

As it stands, this code will work in almost all relevant cases. But, at this point, it can’t function as a drop in replacement for std::setw because it won’t compile if we try to load any type of string with this manipulator.

It Comes With Strings Attached

To load strings using our manipulator, we need three additional functions which will all rely on some variant of the built in function strm.get. Loading a single char is the simplest of the three:
bc..
StrmType& operator>>( char_type& field ) { strm.get(field); return strm; }

To load in a character array, requires a bit more care, though. By default, the streams library concatenates a '\0' on the end of a character array when it loads it in25, so the streams library will load in one less character than you tell it to. To overcome this difficulty, the following code requests an additional character:
bc..
//This is potentially dangerous, as it could go beyond the bounds of field.
StrmType& operator>>( char_type* field ) {
strm.get( field, width+1, StrmType::traits_type::eof() ); return strm;
}

So, any character array that you use with this code must have an extra element present, or it will overflow its bounds. Finally, we’ll load strings proper26:
bc..
StrmType& operator>>( str_type& field ) {
char_type* val = new char_type[width+1];
strm.get( val, width + 1, StrmType::traits_type::eof() );
field = val;
delete val;
return strm;
}

Conclusion and Further Work

Making a streams equivalent of the Fortran read mechanism required more work than I initially expected, and it really would not have been possible without first reading Modern C++ Design by Andrei Alexandrescu.27 That said, this code is only a partial replacement for the Fortran reading mechanism, as there are two things that have not been included. First, if Fortran reads in a numeric field that only contains spaces, it interprets the field as having a value of zero. Including this should require no more than an if statement that tests for this condition wrapped around the call to get( ... ). Second, for floating point fields, Fortran allows you to specify a precision using the format 'Fw.p', where w is the field width and p is the precision, and not print out a decimal point. In the early days of computing this was used as a space saving device. The Fortran code I usually work with prints out the decimal point, so I do not require a similar mechanism in C++, as of yet. However, to include this type of mechanism, I would split the field’s character string into three components: the characters to the left of where the decimal should go, the characters to the right of that point, and the exponent, if it is included. I would then individually load the left-hand-side and the right-hand-sides, each with the exponent in place, and add the results together to get the fully formed field. In other words, 39*103 with a precision of 1 would become 3*103 + 0.9*103. In the meantime, it works as is.

Appendix – Code

I would like to note a couple of things about the implementation. First, the code is not guaranteed to compile with all compilers. While templates were added in 1999 to the C++ standard, not all compilers are yet capable of performing all the manipulations necessary to get them to work effectively. As an example, the version of gcc I have on my Cygwin installation does not like the in place constructor that we used to create both the ignore and setwidth objects. However, the gcc version on my mac (both versions are in the 3.3 line) does not have an issue with it. So, your mileage may vary, and some additional tweaks may be necessary to get this up and running on your system. Second, you’ll note that I’ve placed the code inside of a namespace, wylde, so to reference any of the objects you have to preface your statement with wylde::, e.g. wylde::setwidth(4). Last, if you use this code please include the license statement. Good luck.


/*
License:
Copyright 2007 by Wyldeling.  This code is provided free of charge without limitations as to 
modification and use, with the only exception being that a reference to this original work must be 
retained in any derivative version.  This code is provided without warranty, expressed or implied, 
not even for merchantability or fitness for a particular purpose.
* /

* include &lt;iostream&gt;
* include &lt;string&gt;
* include &lt;iterator&gt;
* include &lt;locale&gt;
* include &lt;cctype&gt;


namespace wylde {
	/*
	 Ideally, I would like a base manipulator that accepts a piece of data and a function.  The function would operate
	 on a stream using the data.  To do this would require the object to be templated for the specific type of stream
	 that it would operate on.  Unfortunately, this knowledge is unkown when the object is created, thus at this point
	 I cannot see a way around it.
	 */
	
	/*
	 The standard format for this type of manipulator has 3 components:  an object to contain the data (complete with 
	 a constructor), an operator (either &gt;&gt; or &lt;&lt;) accepting the object as its second argument which executes the required
	 function, and an inline function that creates the object and is used to place the object into the stream.
	 
	 UPDATE:  Truthfully, the function call is not needed as the object can be constructed by a direct call to its constructor.
	 */
	
	struct IgnoreData { 
		int data;
		IgnoreData(int n) : data(n) {}
	};
	
	template&lt;class StrmType&gt;
	StrmType&amp; operator&gt;&gt;(StrmType&amp; is, IgnoreData idata) {
		is.ignore( idata.data );
		return is;
	}
	
	inline IgnoreData ignore(int n) {
		return IgnoreData(n);
	}
	
	
	struct IgnoreLineData {
		int data;
		IgnoreLineData(int n) : data(n) {}
	};
	
	template &lt;class StrmType&gt;
	StrmType&amp; operator&gt;&gt;(StrmType&amp; is, IgnoreLineData ild) {
		std::string tmp;
		for(int i = 0; i &lt; ild.data; i++)
		{
			std::getline(is,tmp);
		}
		return is;
	}
	
	inline IgnoreLineData ignoreLines(int n) {
		return IgnoreLineData(n);
	}
	
	inline IgnoreLineData ignoreToEOL() {
		return IgnoreLineData(1);
	}
	
	
	template&lt; class StringType &gt;
	class String_num_get : public std::num_get&lt; typename StringType::value_type, typename StringType::const_iterator &gt; {
		public:
			String_num_get() : std::num_get&lt; typename StringType::value_type, typename StringType::const_iterator &gt;() {}
			~String_num_get() {}
	};
		
	template &lt; class StrmType &gt;
	class FixedWidthField {
		StrmType&amp; strm;
		int width;
		
		public:
			//typedefs created for convenience
			typedef typename StrmType::char_type char_type;
			typedef typename std::basic_string&lt; char_type &gt; str_type;
			typedef typename str_type::const_iterator str_iter;
			typedef typename std::num_get&lt; char_type, char_type* &gt; ng_type;
			
			//Constructor
			FixedWidthField( StrmType&amp; s, int w ) : strm(s), width(w) {
				if( !std::has_facet&lt; ng_type &gt;( strm.getloc() ) ) {
					std::locale loc( strm.getloc(), new ng_type );
					strm.imbue( loc );
				}
			}
			
			//The whole point of this excersize
			template &lt; class FieldType &gt;
			StrmType&amp; operator&gt;&gt;( FieldType&amp; field ) {
				//allocate space from the heap for this
				char_type* val = new char_type[width+1];
				char_type* begin = val;
				char_type* end = val + width + 1;
			
				strm.get( val, width + 1, StrmType::traits_type::eof() ); // pull our value off the stream
				
				//trim the surrounding whitespace
				while ( std::isspace( *begin ) ) {begin++;}
				while ( std::isspace( *(end-1) ) ) {end--;}
				
				std::ios_base::iostate state = strm.rdstate(); 
				bool eofset = strm.eof();  //get eof state, and save for later
				
				//this changes state as needed.
				std::use_facet&lt; ng_type &gt;( strm.getloc() ).get( begin, end, strm, state, field);
				
				//recover allocated space
				delete val;
				
				//the get operation sets the eof flag erroneously (at least to us), so
				//if not set after read from stream, then remove from state
				if( !eofset ) {
					// ~ is the complement operator, e.g. ~1101 == 0010
					//therefor this will be all ones except for the eofbit.
					state &amp;= (~std::ios_base::eofbit);
				}
				
				//install the new state; this will throw an exception if the state is not good and the exception flag 
				//has been set
				strm.setstate( state );
				
				return strm;
			}
			
			//Not fully cognizant of all the templating rules, but gcc gives errors for template specialization of member functions
			//need to look up why that is.
			StrmType&amp; operator&gt;&gt;( char_type&amp; field ) { strm.get(field); return strm; }
			//This is potentially dangerous, as it could go beyond the bounds of field.  
			StrmType&amp; operator&gt;&gt;( char_type* field ) { strm.get( field, width+1 ); return strm; }
			StrmType&amp; operator&gt;&gt;( str_type&amp; field ) { 
				char_type* val = new char_type[width+1];
				strm.get( val, width + 1, StrmType::traits_type::eof() );
				field = val;
				delete val;
				return strm;
			}
			
	};
	
	
	struct setwidth {
		int width;
		setwidth(int n) : width(n) {}
	};
	
	
	template &lt; class StrmType &gt;
	FixedWidthField&lt; StrmType &gt; operator&gt;&gt;( StrmType&amp; strm, setwidth sw ) { return FixedWidthField&lt; StrmType &gt;( strm, sw.width ); }
						  
}

1 International Obfuscated C Code Contest (http://www.ioccc.org/, accessed 25 Jan 2007)

2 For example Blitz++ (http://www.oonumerics.org/blitz/Blitz++++, accessed 25 Jan 2007) and μBlas (http://www.boost.org/libs/numeric/ublas/doc/index.htm, accessed 26 Jan 2007)

3 See a Blitz++ example (http://www.oonumerics.org/blitz/examples/Blitz++/matmult.cpp, accessed 25 Jan 2007) that demonstrates the use of familiar mathematical notation for defining matrices.

4 A Dr. Dobbs paper (http://osl.iu.edu/~tveldhui/papers/DrDobbs2/drdobbs2.html, accessed 25 Jan 2007) on how Blitz++ compares to Fortran code.

5 Specification for ifstream (http://www.cplusplus.com/reference/iostream/, accessed 25 Jan 2007)

6 Specification for the default manipulators (http://www.cplusplus.com/reference/iostream/ifstream/, accessed 25 Jan 2007)

7 The setw(int) manipulator from the standard library already performs this function. We’ll have to create something new to extend it to other types.

8 Specification for strings (http://www.cppreference.com/cppstring/index.html, accessed 25 Jan 2007).

9 The C++ Programming Language: Special Edition by Bjarne Stroustrup, p. 261 (http://www.amazon.com/dp/0201700735/omninerd-20, accessed 26 Jan 2007)

10 Ibid., p. 327

11 Streams actually have two template parameters, but by specifying only StreamType I save a little typing, and a compiler should handle it just fine.

12 This works because the compiler identifies the word flush as a function pointer with a specific signature which it knows how to work with. (Stroustrup, p. 631-632)

13 Stroustrup specifies an object that stores both the parameter and the function. This is more generic, and allows for easy extension. (Stroustrup, p. 633) I have opted to take the simpler approach and only store the parameter itself, as done in the stdlib++ (http://gcc.gnu.org/viewcvs/trunk/libstdc%2B%2B-v3/include/std/iomanip?revision=119611&view=markup, accessed 26 Jan 2007).

14 A struct is used instead of a class because everything is public by default, and we’ll be accessing the object’s fields from a non-member function. Alternatively, I could declare the non-member function a friend of the ignore class, but it is such a simple construct that defining it as a struct is more expedient. Also, a constructor has been specified for completeness. Strictly speaking, I did not have to specify a constructor as the compiler will create a default one that does the same thing as mine. However, I like to write one just in case I need to expand it. See the section on FixedFieldWidth where this becomes important.

15 This, and all other code, should be placed inside of its own namespace to prevent name clashes.

16 I chose the latter, so that I can more easily tell when I only want to extract out to the end of the line.

17 Stroustrup suggests a manipulator that takes the variable as a parameter. (Stroustrup, p. 635 – 636) I think this destroys the natural flow of the stream operation, hence the delayed action.

18 I’ve made this a class, as there is no need to provide any access to strm or width outside of the class.

19 Compilers are getting better at working with templates, but this type of manipulation isn’t far from the edge of what they’re capable of. So, any help that we can give them in untangling the mess will save us more than a few grey hairs.

20 Such as using functions like atof from the C standard. Specification for cstdlib (http://www.cplusplus.com/reference/clibrary/cstdlib/, accessed 27 Jan 2007)

21 std::num_get&lt; _CharT, _InIter &gt; Class Template Reference (http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/classstd_1_1num__get.html, accessed 27 Jan 2007)

22 Initially, using a std::string seemed like a good idea, but with the standard library I was using, I could not get all the characters I asked for. The stream would always either strip the leading whitespace, or stop loading when it encountered trailing whitespace. Hence, the move to char* as the iterator type, despite the fun of pointer arithmetic.

23 Note the use of the keyword typename; as the types become more convoluted the compiler often needs help to differentiate between what is a type and an object reference, hence the keyword typename.

24 Strictly speaking, end points to one position beyond the end of the array.

25 The '\0' is used a known topping point for c-style functions, so they don’t go beyond the confines of the array.

26 As I stated in a previous footnote, there is no guarantee that the number of characters returned is the number of characters asked for when loading in a string directly from a stream. Therefore, I had to resort to the lower level strm.get functions, which do have that guarantee, excepting running into the end of file.

27 This is one of those books that ideas just come unbidden to you as you read it. Full title: Modern C++ Design: Generic Programming and Design Patterns Applied (http://www.amazon.com/dp/0201704315/omninerd-20, accessed 31 Jan 2007)

Similarly tagged OmniNerd content:

Thread parent sort order:
Thread verbosity:

wyldeling, could you (or anyone else) shed some light on the use of Fortran in the science community? I’m curious if it’s got special features that other languages don’t have, or if it’s just been used for so long that it’s just become a standard. I don’t use compiled languages too much, so most of my knowledge is in scripting. Anyway, just curious.

I honestly never knew that FORTRAN was still used anywhere. I remember taking a required semester of it in college (20 years ago) along with PASCAL and BASIC, but I just assumed it went by the wayside a long time ago. Incidentally I can remember my teacher telling us that he was a programmer for NASA, and some of the programs he wrote in the late sixties were still in use on the space shuttle at the time.
Reading fixed width data sounds like the type of thing I used to do to convert information from a Hewlett Packard 3000 mainframe computer to PC’s so we didn’t have to rely on the MIS dept for every manipulation of data we wanted. I had taught myself C language at the time, and had some mechanism where I stored the “widths” in a text file which would be read at runtime. If the MIS dept changed the field widths, I only had to change the text file instead of recompiling the program. I am going to try to take a close look at what you’ve done with templates and overloaded operators because it is an interesting approach.

Some of you may have noticed that the article/code changed over the weekend. Much to my chagrin, I found two serious bugs and a peculiarity of gnu c++ std library. The first of the two bugs involved my implementation of ignoreToEOL. The original version just plain could not work. I believe it was because the construct:
bc.. fin >> ignoreToEOL;
p.
was trying to discover to many types at once, and was just getting confused. So, I swapped out that implementation with the a call to ignoreLines(1) which does the same thing, except in order to use it you need to write:
bc.. fin >> ignoreToEOL();
p.
instead. The second, and more serious, type of bug (yes, there was more than one) involved the problem of whitespace.

Naively, I assumed passing in " 98" to num_get.get() would return 98, but it barfed setting the fail bit. Passing in "98 " would do the same. Both of these were fairly common cases, and I didn’t test for them. So, I needed to trim excess whitespace. It was at this point, I found that using a string was a bad idea. The stream would strip off leading and trailing whitespace, but it would leave the trailing whitespace in the buffer to be grabbed by the next field. So, I switched to loading the characters directly into a character array allocated off the heap. This, also, necesitated the change to the fixed width string functions to guarantee that I got the exact number of characters I asked for.

After using this for a little bit, I noticed that it would not compile if I tried to load in an int. It turns out the gnu c++ std lib’s version of num_get does not have a function for loading in a int. It loads in an unsigned int and a long int just fine. But, if you try to load in an int without using my manipulators, it works just fine. So, I assume when you load in an int without using my library, it can perform the implicit cast required, but if you use my library, it cannot. Therefore, don’t use an int with this library use a long instead.

Share & Socialize

What is OmniNerd?

Omninerd_icon Welcome! OmniNerd's content is generated by nerds like you. Learn more.

Voting Booth

America's involvement with the ISIS crisis should be?

6 votes, 0 comments