Am I too Old-School?

I often see examples in the MSDN like:
1
2
3
4
5
6
7
8
9
array<DirectoryInfo^>^diArr = foo->GetDirectories();

Collections::IEnumerator^ myEnum = diArr->GetEnumerator();

while ( myEnum->MoveNext() )
{
	DirectoryInfo^ dri = safe_cast<DirectoryInfo^>(myEnum->Current);
	Console::WriteLine( dri->Name );
}


As much as I like object oriented programming, I don't really get the point of enumerators, particularly in the case above.

What's wrong with neat little fast loops?

1
2
3
4
5
6
array<DirectoryInfo^>^diArr = foo->GetDirectories();

for(int i=0; i < diArr->Length; ++i)
{
	Console::WriteLine( diArr[i]->Name );
}


I can't believe that a call like MoveNext() is faster than ...->Length.
Also, I don't think ...->Current on the enumerator is faster than ...[i] on the actual array.

So why all that extra creating of objects, casting and calling around as if the CPU is bored all the time?
Am I too old-school or do I miss something?

Am I too old-school or do I miss something?

CPU time is nowadays far cheaper than time of programmers, so one can often encounter usage of specific programming patterns where they are supposed to improve code flexibility etc.

Collection could be not only array - is not it so? And this code could be easier updated to usage of other implementation of collections. In bigger projects it is preferable to keep code so that it is easier to refactor, though probably not extra efficient.
Microsoft code isn't exactly the most concise or necessary. I think it depends on your needs. I think the simple but easily modifiable and understood intuitive approach is the best. How is anyone supposed to decode their usage of the "^" operator without looking at documentation?
Last edited on
Enumerators/iterators make no assumption about the storage of the data. A MoveNext style function can iterate through a virtually infinitely large sequence of data without concern of where that data is contained.

An [indexing] approach may be faster to step through, but requires that all the information be stored sequentially in a linear array...which may not be the case with something like a directory listing. Which means to get that directory listing... the data needs to be iterated over, then copied to a linear array.

So yes... while [indexing] can be faster to access... it may also use more memory and may also be slower overall because of the work involved to build that sequential array.
Last edited on
DeXecipher wrote:
How is anyone supposed to decode their usage of the "^" operator without looking at documentation?
Knowing the language helps.
Last edited on
But that's not actually part of C++; it's part of their non-standard extension/bastardisation called C++/CLI. You can't expect people on a C++ forum to know that any more than you can expect them to know C# (although I would wager C++ programmers would find C# easier to read than C++/CLI because C# is a purrdy language).
All I know is that ^ in place of * makes it an automagically managed pointer. Though I have no idea why it's so pervasive over simply keeping things on the stack. Maybe all their function signatures take ^ instead of by reference :p
@chrisname
The language I was referring to was C++/CLI.
Disch: Your argument is pretty solid, except that in OP's first example, the iterator is for an array, not for a directory.
rodiongork wrote:
CPU time is nowadays far cheaper than time of programmers ...

It's not about costs, it's about the CPU that is available in the target system. If you write code for a traffic control system you can't just put another CPU (or a different one) in the rack if your code is to slow.

Disch wrote:
A MoveNext style function can iterate through a virtually infinitely large sequence of data without concern of where that data is contained.

Good point. But the directory listing was just the example I recently stumbled upon. I usually use STL and boost where a lot of example code recommends iterators as well.
The enumerators and iterators seem rather similar; they just use different syntactic sugar. Are the enumerators a bit more fat?
1
2
myEnum->MoveNext();
++myIter;

Qt had initially for its containers only Java-style iterators http://qt-project.org/doc/qt-4.8/qlistiterator.html
but has added STL-style iterators too to improve compatibility with STL algorithms.

If the container happens to be a linked list, it could still provide operator[], but a loop with iterator is more efficient.
The main drawback of lists and forward_lists compared to these other sequence containers is that they lack direct access to the elements by their position; For example, to access the sixth element in a list one has to iterate from a known position (like the beginning or the end) to that position, which takes linear time in the distance between these.

If the list does not have the operator[], then one cannot write the "old-school" loop, and a change of container will affect syntax of code.
Good point. But the directory listing was just the example I recently stumbled upon. I usually use STL and boost where a lot of example code recommends iterators as well.


Iterators work uniformly across all containers.

[] operator only works as indexing on a few.

If you are using iterators and you decide to switch from vector to list... then you don't have to change anything.

If you are using [] and you decide to switch from vector to list, you have to rewrite all your code which accesses it to use iterators because [] isn't available for lists.

Using iterators with vector is not necessarily slower or faster than using [].



So really it comes down to... "using [] has some downsides, but iterators always work.. so why not use them whenever you can?".

It's the same principle as preferring prefix ++ over the postfix ++.
Also, if you use iterators, then the data can be streamed instead of being loaded all at once, with the calling code never being able to tell the difference. There's lots of scenarios where this would be useful, such as operations on files that are larger than the amount of available memory, or data coming from the network or other devices where waiting for it all to come at once might block the process for an unreasonable amount of time.

Disch wrote:
Iterators work uniformly across all containers.

[] operator only works as indexing on a few.

std::stack doesn't support either iterators or indexing. You can only operate on the top of a stack.
Last edited on
Yeah but std::stack is stupid.
It does seem kind of weird for it to just be a wrapper of another container that reduces functionality.

It's not about costs, it's about the CPU that is available in the target system. If you write code for a traffic control system you can't just put another CPU (or a different one) in the rack if your code is to slow.

I fear I failed to explain.

We usually can assess the speed of the code by our experience. Calling the method instead of direct access to field is not great overhead if it is performed no more than million time per second.

If you read "Effective Java" by Joshua Bloch (who was far not the last person in Google) you will see very strong advice "Do not optimize until you see it is really necessary".

Otherwise, why writing in C++ at all? Let us master assembler (I know three of them and I spent significant time writing low-level optimization some years ago).

But you will find that the program performs some operation (like balance recalculation) in 30ms instead of 100ms using the CPU available to your customer. Customer will not see such "improvement" but you will spent 2-5 times more time on this program and you will ask for 2-5 times more salary.

The customer will shrug his shoulders and will hire PHP programmer. The same operation will take 500ms now and for the same price programmer will be able to add more bonus features.

Of course optimization is important in some critical places - but in most cases it is sacrificed for the sake of testability, maintainability etc.
Last edited on
@Disch, LB
I don't see what's weird or stupid about it, and the same for std::queue. I see it as a specialisation. If I need only FIFO or LIFO behaviour then I use something that only provides that behaviour and nothing else. There are no nasty surprises that way -- imagine you were using a vector as a FIFO and someone decided to start adding things to the front of it. If the order was actually important, it would mess everything up. Plus I think it's good for classes to only expose the behaviour that you need. It makes for stronger encapsulation.
@chrisname I definitely think there are cases to have that limitation on functionality, but I just find it odd that this be done via adapting another container. Is there really not a more efficient way to have a stack?
Depends what you mean by efficient. Now watch as I magically converge this thread of discussion with the other one: implementing containers from scratch might prove more efficient in terms of processor-time, but not in terms of programmer time, and, as rodiongork will tell you, processor time is cheaper than programmer time.

[edit] Also, depending on the compiler's ability to optimise, there probably isn't much difference in performance. I'm guessing std::stack is implemented as a very thin wrapper, maybe something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template <typename T, class Container = std::deque>
class stack {
public:
    stack() { }

    // ... more constructors ...

    inline bool empty() const { return container.empty(); }

    inline std::size_t size() { return container.size(); }

    inline T& top() { return container.back(); }

    inline void push(const T& value) { container.push_back(value); }

    inline void push(T&& value) { container.push_back(value); }

    inline void pop() { container.pop_back(value); }

    inline void swap(const std::stack<T, Container>& other) noexcept { container.swap(other.container); }
private:
    Container<T> container;
};

and similar for std::queue. As you can see, all those methods are a single line and can easily be inlined. Even if I hadn't declared them inline, the compiler would probably do it anyway. All it would cost you is a little extra compile time for the compiler to manage the inlining, after which there would be no performance hit at all. Even if the compiler chooses not to inline those functions (which would be mad since they're all one-liners) the performance hit would be minimal, it's just function call overhead, which is nothing compared to the time required to resize the container for push()/push_back() if you didn't pre-allocate your stack. [/edit]
Last edited on
I guess that what I meant is that since the stack just wraps another container, it makes it harder for the compiler to optimize based on usage.
Topic archived. No new replies allowed.