Iterative Ranting

posted in The Enigma Code
Published November 19, 2006
Advertisement
There was another mini SC++L versus do-it-yourself debate in the Beginners forum this week, which got me thinking (again) about the issue of vector and reading from a file. The issue is that there is a simple solution using new which cannot be expressed as efficiently using vector:
std::ifstream reader("file.dat", std::ios::binary);reader.seekg(0, std::ios::end);std::size_t data_size = reader.tellg();boost::scoped_array< char > data(new char[data_size]);reader.read(&data[0], data_size);
The closest you can come is to use istreambuf_iterators:
std::ifstream reader("file.dat", std::ios::binary);std::istreambuf_iterator< char > begin(reader);std::istreambuf_iterator< char > end();std::vector< char > data(begin, end);
which performs multiple allocations and copies within the vector constructor for all but the smallest of files since istreambuf_iterator is only a model of InputIterator. Alternatively you can pre-allocate and read:
std::ifstream reader("file.dat", std::ios::binary);reader.seekg(0, std::ios::end);std::vector< char > data(reader.tellg());reader.read(&data[0], data.size());
which redundantly initialises the entire vector to zero before overwriting with the file contents.

Let's be clear. This is not a major issue from a performance point of view. We're talking about file I/O, which is orders of magnitude slower than anything that vector is doing. The issue is simply that there is a clear deficiency in the standard library, and worse, one without apparently any good reason for being there.

I messed around a bit looking for alternative solution using vector. It seemed clear to me that an iterator based solution would be cleanest, and that the only real problem with istreambuf_iterator is that it is unnecessarily generic. File streams support random access, whereas generic streams only support input/output iteration. I tried writing my own random access ifstreambuf_iterator and discovered that I couldn't. More surprisingly, the reason I couldn't implement it is because the C++ iterator abstraction is fundamentally broken. An istreambuf_iterator is not a model of RandomAccessIterator. It's a model of RandomAccessInputIterator, a concept unsupported by the C++ iterator abstraction model. The C++ iterator abstraction combines two orthogonal concepts, iteration and read/write access, into a single concept, without supporting all possible combinations.

Armed with this new-found knowledge I trawled the web (well, I typed a couple of brief queries into Google, but the former sounds more impressive) and unsurprisingly discovered that I was not the only one to have come to this conclusion. Reassuringly there is already a paper at the C++ Standards Working Group site that, also unsurprisingly, is much better thought out and goes into much more depth. The vector-from-file issue is one that has bugged me for quite some time. Now I can finally stop wondering if I'm missing some simple construct and rest in the knowledge that it simply needs fixing.

One of my co-workers stumbled across something interesting at work this week that had a few of us scratching our heads. Without consulting a compiler, what would you expect to result from the following code snippet?

#include struct Base1{	Base1()		:		a(1)	{	}	int a;};template < typename Type >struct Base2{	Base2()		:		b(2),		c(3)	{	}	void function()	{		std::cout << b << '\n';	}	Type b;	Type c;};struct Derived	:	public Base1,	public Base2< int >{	void function()	{		Base2::function();	}};int main(){	Derived d;	d.function();}

?nigma
Previous Entry De frmenta ging...
0 likes 3 comments

Comments

mrlachatte
I would except that the compiler would complain that Derived has multiple function()s, or the call is ambiguous or something. I gather that's not what happened.
November 20, 2006 07:31 PM
Enigma
Not a bad guess. My guess before encountering this would have been one of:
  • On first reading, expect the code to print the output 2.

  • On second reading, expect the code to fail to compile with an error that Base2 is not a valid identifier (you can only use a template name without template parameters within the definition or a specialisation of that template class).

Borland 5.8.2 and GCC 3.3.1 agree with my second reading:

Error E2102 example.cpp 37: Cannot use template 'Base2<Type>' without specifying specialization parameters in function Derived::function()
Error E2379 example.cpp 37: Statement missing ; in function Derived::function()

example.cpp: In member function `void Derived::function()':
example.cpp:37: error: use of class template `template<class Type> struct Base2' as expression
example.cpp:37: error: syntax error before `;' token

Visual C++ 8.0 on the other hand goes for option three. Literally. It compiles with no errors and produces the output 3. It appears that there is a compiler bug which accepts the incorrect explicit scoping and then generates an incorrect this * offset. As you can imagine, this was quite an interesting bug to track down in real code.

Σnigma
November 24, 2006 12:13 PM
mrlachatte
That's an impressive find!
November 26, 2006 07:17 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement