Jump to content
Sign in to follow this  
  • entries
    24
  • comments
    21
  • views
    17652

Iterative Ranting

Sign in to follow this  
Enigma

298 views

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
Sign in to follow this  


3 Comments


Recommended Comments

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.

Share this comment


Link to comment
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

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!