Kuro5hin.org: technology and culture, from the trenches
create account | help/FAQ | contact | links | search | IRC | site news
[ Everything | Diaries | Technology | Science | Culture | Politics | Media | News | Internet | Op-Ed | Fiction | Meta | MLP ]
We need your support: buy an ad | premium membership

[P]
Musings on Good C++ Style

By GoingWare in Technology
Fri May 10, 2002 at 03:08:38 AM EST
Tags: Software (all tags)
Software

Space Monkey said C++ is an abomination to society, and is doubtlessly responsible for hundreds of millions of lost hours of productivity. While I don't agree with him, I understand very well why he and many others would feel that way, as much of the C++ source code that I have seen is indeed very poor.

I address this and other issues in some programming tips I have written over the years. Recently I completed a major revision to my article on C++ style that focuses on the storage and representation of data: Pointers, References and Values - Passing Parameters, Returning Results, and Storing Member Variables, with Musings on Good C++ Style.


Pointers, References and Values is very long so I cannot just reproduce the whole thing here. If you read the original, I suggest you take more than one sitting. But I provide a summary here of some of the more important points. I apologize that even this condensed version is so long, but it is a complex topic.

The Main Goals

The main goals in my approach are to write correct, bug free, high performance and maintainable code. The code must be easy to work with during development. Sometimes these require a choice among trade-offs; in general one must prefer correct and bug-free, then maintainable and easy to develop, and finally high performance.

You may be surprised to hear me list performance as the last thing to be concerned about in designing one's software, but I'm not advocating slow code; rather, I'm acknowledging that most of the code one writes has little effect on the ultimate performance of the product. This is the 80-20 rule; 80 percent of the runtime is spent in 20 percent of the code - but you're likely to find that most of the complexity is in the 80 percent of the code that doesn't affect performance significantly, so you might as well make it maintainable rather than trying to make it fast.

It is almost always possible to write nice code that runs fast. This requires experience, judgement and skill. I feel that the style I advocate encourages efficiency throughout the program.

One of the strengths of C++ is also one of its greatest difficulties - the wide array of choices it presents for how things may be implemented. Should a class use inheritance or composition? Will inlining this function really improve performance? Shall I encourage reuse through polymorphism or templating?

Many other languages are simpler to learn in part because they limit one's choice. Usually the defaults available are not bad but I feel they limit the best one can hope to achieve. In this article I discuss the commonly encountered choices for data storage and representation, and in doing so hope to make C++ as easy for you to use as these other languages.

I was dismayed early on to read someone's opinion that it takes three years to learn to program effectively in C++, but three years after I began regular C++ coding I decided I felt that way too. The best chance one has to become an expert faster is educate oneself continuously. I wrote Pointers, References and Values and this K5 article because I hope to help others achieve their best.

Avoiding Unnecessary Header Dependencies

A common problem in C and C++ development is that changing a single header file will force much of the project to rebuild. Excessive compile times caused by a geometric explosion of nested header file processing is another. These can be avoided by #includeing a header in other headers only when absolutely necessary. In general headers should only be included in the .cpp implementation files that use them, and not in other headers.

Headers should only include other headers that are absolutely required to get them to compile. You can test this by #includeing a header in a .cpp file all by itself and compiling that. A good practice is to include a class declaration header as the first non-trivial line in the .cpp file that implements the class.

Don't include unnecessary headers as a convenience to users - that ultimately turns out to be no convenience at all because it greatly increases overall compile time and forces unnecessary rebuilds.

Conversely, don't require headers to be included in other files before yours can be; headers should compile on their own and in any order.

You can often avoid the need to include the headers that have full class and struct declarations by using forward references to declare incomplete types:

class Foo;
struct Bar;

The compiler does not require the full declaration, and you can use the incomplete type, when it needs to know nothing but the name of a symbol. That is, it does not need to know its size or the calling conventions of any of its members or the names or types of any of its member variables.

If you store a variable by value, the compiler will need to see the full declaration first to know its size. Otherwise it cannot know how much space to provide when allocating the enclosing class' objects.

For this reason, prefer storing member variables by allocated pointers. You also usually store a pointer when the member is being used polymorphically.

There are some cases where it is not possible to declare an incomplete type, as with typedefs, templates and member classes, and cases where you have to do a little extra work. I discuss these in more detail here.

When to Store Members by Value

While storing a member by value requires its full declaration to be known, it has the advantage that it avoids the time and space overhead of heap allocation. You should discourage the allocation of lots of small objects from the heap because the management overhead of record keeping in the heap may be considerably more than the payload data.

If your whole class has few members and all those members are small, then it may be advantageous to store the members by value. Of course, you will usually store built-in types by value.

Initializing Member Variables

C++ initializes member variables in the order that they are laid out in the class declaration. It destructs value member variables in the reverse of this order. Thus you must initialize your member variables in the constructor's initialization list.

If you omit the initialization list and instead set up your members in the constructor body, C++ will initialize your members to their default values anyway. This may not be what you want, and will increase your code size and slow your program down when you initialize the members a second time.

An initialization list follows the prototype in the constructor implementation but comes before the opening curly brace as follows:

// Foo.h
#include "Bar.h"

class Foo: public Bar{

public:

Foo( int inVal );

private:

int mVal;

}

// Foo.cpp
#include "Foo.h"

Foo::Foo( int inVal )
: Bar(), // construct the base class
mVal( inVal ) // construct the member
{
// Note the constructor body is empty
}

Always initialize your member variables in the list in the same order that they are declared in the class declaration. C++ will initialize them in this order regardless of how you try to, and ordering them consistently will keep things clear and have the advantage that you can initialize later members in terms of earlier ones. Getting the order wrong may initialize your members with whatever garbage was left lying around in memory.

If you wish to call a class method to calculate a parameter for the initialization list, declare it static and do not pass this. You must not do so because the object is not yet completely constructed. You may call methods in other classes, or in the base class, because it has been completely constructed by this time.

The constructor body should only include code that cannot possibly be put in the initialization list, for example error checking that might throw an exception. In general you should strive to have empty constructor bodies.

. Smart Pointers

Memory leaks are the scourge of the C++ programmer. You may try to minimize them by methodically inspecting your destructors, and comparing them to your member variable declarations, to ensure that you delete every pointer that you own. However, it may be hard to keep ownership straight, and there are deficiencies with this approach.

Exceptions cause the problem. If your program throws an exception all the objects that have been fully constructed by value between the try and the catch that receives the exception will be destructed. Allocated pointers and the objects referred to by references will not be. It is possible to use lots of try/catch pairs to deal with this but it is tedious and error prone.

If an exception is thrown during the processing of an initialization list, the object being initialized will not be destructed. To do so would be to call the destructor on an incompletely initialized object, and might cause a crash, for example if pointers are deleted that contain garbage values.

We also have the problem of how to indicate ownership. Sometimes we have a pointer because it is provided for our use by the real owner, rather than for us to manage and eventually delete.

The solution to this is to use smart pointers. There are different kinds of smart pointers, but they are generally used by value as members or local variables, and they override operator->() and operator*() so that they can be used with pointer semantics. They automatically dispose of the pointer they hold, but have different policies for doing so.

Because of the possibility of exceptions in initialization lists, I am not convinced it is possible to avoid memory leaks in ISO compliant C++ code without the use of smart pointers.

Indicate ownership by using a smart pointer. Indicate non-possessive use of a pointer by just holding a naked pointer.

The simplest smart pointer, and the only one provided by the Standard Library, is auto_ptr. Pass auto_ptr's constructor a pointer that you have allocated. It will be deleted when it goes out of scope, even if an exception is thrown. Store auto_ptrs as member variables by value in your classes, and then simply omit them from the destructor.

Strive for empty destructor bodies. The bodies won't really be empty, but have a lot of hidden code that is generated automatically by the compiler. The usual reason to have nontrivial destructors is to free resources that are not allocated with new, such as to close files or free memory allocated from the operating system rather than with new.

If your destructor body is empty you can omit it. But I suggest providing one anyway; it does not add extra code as C++ will provide if you omit one, and destructors are good places to set breakpoints during debugging.

auto_ptr has some problems. You cannot use auto_ptr for arrays because they require delete[] rather than plain delete (I discuss that problem here). It has the advantage that is is as fast as manually deleting, while avoiding the memory leaks.

It is not possible to provide decent copy and assignment operators for it because both instances will delete the pointer and corrupt the heap. The compromise settled upon in the C++ standard is for a recipient auto_ptr to take ownership of the memory from the original.

This is very surprising to most users and prevents its use in Standard Template Library containers, as well as STL containers of classes containing auto_ptr members unless they provide explicit copy constructors and assignment operators (which may not be possible).

The solution is to use smart pointers that share the memory resource. These use reference counting to know when it is time to delete the memory. When a reference counted smart pointer is assigned or copied, the reference count is incremented. When one is destructed, the reference count is decremented. When the reference count reaches zero, the pointer being managed is deleted.

There are a number of different kinds of reference counting pointers. A popular one is the boost::shared_ptr provided by the Boost C++ library. Another is the thread-safe ZRef template written by Andy Green as part of the ZooLib cross-platform application framework. The most flexible are the family of policy-based smart pointers written by Andrei Alexandrescu in the Loki library. These are described in his book Modern C++ Design.

Finally, I should mention boost::scoped_ptr. This is a non-shared pointer template meant simply to delete pointers when it goes out of scope. It cannot be copied or assigned. If you use Boost, choose scoped_ptr rather than auto_ptr if you do not need reference counting.

Because of the availability of better smart pointers, the only reason to use auto_ptr is that you want to avoid dependence on any libraries exception those supplied by your compiler vendor. There is some possibility that a future C++ ISO Standard version will include reference counting smart pointers. The improvement of the Standard Library is one of the reason for Boost's existence.

Prefer Passing Parameters by Const Reference

Passing parameters by reference rather than by value saves on the stack memory required to store them and the time required to construct and destroy them.

Passing by reference also avoids the slicing problem, that is, the construction of a base class object by passing a subclass object to a function expecting its base. Slicing loses the polymorphic behaviour of subclass objects. Attempts to use a value parameter whose class has a pure virtual method will fail to compile, as you cannot construct abstract base class objects at all.

Indicating Object Existence

One should indicate that an object is required to exist by using a value or reference and that it is optional by using a pointer that may be nil. For example, pointers may be used as parameters with the understanding that a nil value may be supplied. It would then be up to the recipient to test its value.

Function parameters whose originals can be changed should be passed as non-const reference when they are assurred of existence and pointer otherwise. This convention is not widely followed but for C++ use I feel it is better than the C-derived practice of passing pointers for mutating parameters because passing a reference avoids the need to test for NULL.

You can store a member variable by reference if you want the object to operate on an original that is guaranteed to exist rather than a copy. It can be either const or non-const depending on the degree of access desired. But note that because references cannot be rebound, you may not be able to supply an appropriate assignment operator (and the compiler cannot generate a default one).

Two alternatives would be to only allow assignment between objects that hold references to the same object (throwing an exception otherwise) or to use a pointer member instead.

If a member variable may be replaced during the lifetime of the object that has it, you must store it by value or pointer rather than by reference.

When to Pass Parameters by Value

Paradoxically, performance is also the reason to pass parameters by value. This will be the case when the object is very small, as with built-in types, or that have fast constructors - the object's members are few and are all held by value, especially when the constructor is inlined.

One reason is that references are usually implemented as memory addresses that are automatically dereferenced for you. If an object is small enough that it can be held in a register, passing it by pointer or reference will require that it be copied to RAM and the RAM address passed instead. The cost is double with binary interfaces that pass parameters in registers like as is usually the case with PowerPC.

Prefer Returning Results by Pointer or Value

The value returned by a function must continue to exist after the function exits, so you usually want to return a result by value. Sometimes you might allocate a pointer in a function and return that, with the understanding that the caller takes ownership and is responsible for deleting it.

It is a grievous error to return a pointer or reference to a local variable. It might actually work sometimes, if the memory is not overwritten before you are done with it, but it will eventually cause you to crash, or worse, to get behaviour that is difficult to understand when the stack memory is overwritten with new data.

When to Return a Reference

You can return a reference when the value it refers to will be guaranteed to exist after the function exits (and hopefully as long as it is needed). One reason you might return a reference is for performance - you avoid the time and memory required to construct a result by value. Another is that you want to allow operations on the original rather than a copy.

Many operators such as the assignment operator should return a reference or const reference to *this.

I also find reference results very useful in base classes when providing protected accessors for subclasses, as well private accessors for internal use by any class.

Reference results are error prone as the caller may depend on their existence longer than they are valid. The least troublesome use of references are as function parameters since you can reasonably count on a parameter being valid until function return. The use of reference member variables and function results should be considered somewhat suspect and subjected to extra testing.

How to Throw and Catch an Exception

You should throw an exception by value or reference. It is permissible to throw references to local variables because the exception object actually caught will be a copy of what you throw yourself.

You should catch exceptions by reference or const reference. This avoids the slicing problem that also occurs when passing parameters by value.

You should not throw pointers. A value exception object (or the one constructed by a thrown reference) will be destructed when exception processing is done, but pointer exceptions will not be deleted.

You might try manually deleting the pointers in your catch clauses but this will corrupt the heap if someone throws you the address of a static object. Depending on this is also a source of memory leaks. You could avoid deleting any pointers caught but then you will leak memory when some later code finds good reason to allocate an exception object.

Importantly, this means you should not throw C strings:

throw "Bailing out!"; // don't do this

The Value of Constant Factor Optimization

Software Engineering and Constant-Factor ... optimizations are completely orthogonal.
Space Monkey

There is a certain value in constant factor optimization, as well as optimizing the low-order terms in an algorithm. Understanding this is one of the reasons I am able to make my programs run so well.

Algorithm analysis in computer science traditionally teaches us to consider the asymptotic time and memory behaviour of algorithms that operate on single, very large data sets.

For example, when sorting files with millions of records, the term of the runtime formula with the highest exponent will eventually dominate. The constant factor in that term is considered negligible compared to the cost of adding just a few more records, unless the run time is linear.

However, this is not the only kind of data we are called on to process. Sometimes we have many small, independent data sets. This is commonly encountered in both GUI and operating system programming.

To continue the discussion of sorting, consider how to find the best algorithm to sort 100,000 data sets with about ten records apiece.

Well, the answer is that nearly any algorithm will do, but the one that wins will be the one that is efficiently coded.

One might try to combine the data sets into a composite and find a low-order algorithm to operate on that, but in GUI's and operating systems one must often complete the processing of one small data set before obtaining another. They may also arrive unpredictably.

Consider the run time equation:

run time = An^3 + Bn + C

This described an order n-cubed algorithm, a horrible implementation choice, and clearly one that no amount of optimization will help!

But what if n were 5, and C were a million? Then I would suggest this is not such a bad algorithm, but it would be well worth your while to optimize its implementation.

Alternatively, suppose we really are considering a large data set so that the lower order terms recede in importance, and we already have the best algorithm but n is so large that the average runtime is two hours. Then it would be well worth your while to optimize the implementation to cut the high-order term's constant in half, so that you could save your users an hour (on the average) each time they ran your application. I think your users would consider constant factor optimization worth your time.

Efficient coding is often neglected in computer science education, but I think its value must be reasonably well known. Otherwise Bjarne Stroustrup would not have included inline functions in C++, and the Linux kernel developers would not use the fastcall calling convention to pass parameters to system calls. In fastcall, the parameters are passed in registers rather than the stack, as is usually the case on Intel architecture.

Most of the style guidelines presented here have the effect of being constant factor optimizations. The exception is the use of reference counting, as the alternative to it would be to make a copy the managed memory each time it is needed, rather than sharing a single one; reference counting actually reduces the order of the algorithms it is used in.

It is rare to experience much benefit from changing the parameters of a single function call from values to references, but applied consistently throughout an application the speedup will be substantial.

Old School Programming

I learned to use a profiler in my first career programming job, where I was writing image processing software on two 16 MHz 68020 Sun 3/160 workstations. These machines cost $40,000 apiece, had four megabytes of memory, and one ran diskless because hard drives were so expensive.

The time spent processing our images was so lengthy that we invested five thousand dollars in a floating point accellerator card for one of the workstations. This was a full-sized VME bus card whose core was a 68881 floating point coprocessor, plus a lot of support circuitry, and was advertised to double the speed of numeric programs.

With the addition of the FPA, the time required to calibrate an image was reduced from 90 seconds to 85. I knew that I would not be working there long unless the runtime was cut in half by the time my boss returned the next morning, so I spent some time digging through the Sun manuals to see what I could do about it, and I discovered the profiler.

I was impressed to discover that the bulk of the processing time was spent in two functions. One obtained the standard I/O FILE * that the next pixel would be read from, and the other was a wrapper around a getc() that would fetch a single pixel. The first function returned the same result a quarter million times per image. (This was not my fault! The image processing library we used was built around the use of stdio to process images through Unix pipelines.)

When I changed the implementation to avoid most of the library and just read the whole image in a single read() system call, the runtime of the program was reduced to ten seconds without the FPA and five seconds with it. So Sun's claims turned out to be correct - but I was still left with a lot of explaining to do.

Hoare said "Premature optimization is the root of all evil in programming." - and this statement is widely quoted by those who believe it (it is usually misattributed to Donald Knuth, who himself gave Hoare proper credit).

However, I do not feel that this is strictly true. The best performance must be both designed in from the beginning, like thread and exception safety, and it must be maintained through conscious application by each working engineer throughout the software development process.

This is not to say one should sit around minutely tweaking code before you have anything working (here I would agree with Hoare). Rather, one should plan the overall design of one's product to use efficient algorithms, to minimize memory consumption, virtual memory paging, cache consumption and overall code size.

Failure to pay attention to performance throughout the development process leads to the slow, bloated and buggy software that has come to be the norm in the consumer market today. Despite the valiant effort of the hardware engineers to uphold Moore's Law, lazy and ill-informed programmers (or perhaps good ones constrained by uncaring management) work even harder to reverse the progress, leading to the phenomenon of ordinary office and home users having to replace their hardware every three years just to continue performing basic productivity tasks with currently available programs.

I discuss a couple of my most valuable performance tuning tools in the conclusion of Pointers, References and Values - studying the assembly produced by compiling your C++ source, and counting the number of machine instructions required to trace through a function and all its subroutines.

These methods are pretty controversial; I have long argued with object oriented purists about them and some who have read the methods responded by ridiculing them. But I stand by what I advocate.

I am not suggesting that one compile one's source to assembly and then optimize the assembly. (Although I have done that too, and there are important reasons to do this, as in embedded programming.)

Rather I am advocating that one understand the ultimate effect that one's source code has on the real hardware it will run on. Especially with object oriented languages, there is extra code generated by the compiler that you do not see in your source, and understanding what this is will aid you tremendously, even if you do it only once. Rather than tweaking, understanding the reasons behind the output of the compiler is the benefit of reading the assembly.

Two important cases of this in C++ are temporary objects and function call overhead. Optimizing these away is completely portable as will be many of the other things you discover from these practices.

If a function takes a reference parameter and you pass it a a different type, the compiler will construct a temporary object on the stack and pass a reference to that, then destruct the object when the function returns. This is one of the most significant performance traps in C++.

The run time for functions consisting of only a statement or two may be dominated by the time required to pass its parameters, set up a stack frame, tear it back down and return the result. If a function is called extremely frequently, you may benefit from inlining it, and doing an instruction trace will help you find the best functions to inline.

You should not inline too many functions because it increases your overall code size (increasing VM paging) and prevents the creation of "hot spots" in the cache. A common problem with software written by programmers that do not understand the constrained size of their processor's code and instruction caches is that the cache may end up slowing down the code compared to the performance if the cache was disabled or else the code was implemented with cache efficiency in mind.

The Apollo 11 lunar module got the astronauts safely to the moon's surface and back with 32 kilobytes of magnetic core memory. That's very old school. I think we need to get some of that back.

I leave you with this word of advice:

Invest in good technical books, whatever your language. Never stop buying and reading them; if you don't have much money, buy them used or borrow them from friends or a library. You can find excellent reviews for many programming books at the ACCU Reviews Section.

Thank you for taking the time to read this.

Sponsors

Voxel dot net
o Managed Hosting
o VoxCAST Content Delivery
o Raw Infrastructure

Login

Poll
What language can the best programmers write the best software in?
o Smalltalk 3%
o Java 5%
o C++ 12%
o Python 8%
o Scheme 7%
o C 11%
o Assembly (any architecture) 3%
o Any language, when you're the best 48%

Votes: 213
Results | Other Polls

Related Links
o Space Monkey
o said
o programmin g tips
o Pointers, References and Values
o Headers should only include other headers that are absolutely required to get them to compile
o here
o initialization list
o auto_ptr
o here
o reference counted smart pointer
o Boost
o ZooLib
o Modern C++ Design
o Boost's
o Space Monkey
o Hoare
o widely quoted
o Moore's Law
o conclusion
o object oriented purists
o But I stand by what I advocate.
o old school
o ACCU Reviews Section
o Also by GoingWare


Display: Sort:
Musings on Good C++ Style | 145 comments (136 topical, 9 editorial, 0 hidden)
Nothing but good stuff. (4.50 / 4) (#7)
by BigZaphod on Thu May 09, 2002 at 10:42:06 PM EST

I caught your previous attempt to post this and took a gander at the full article you wrote.  I spent quite awhile reading it and letting it all sink in.  I then proceeded to go back and revist some of my code I happened to be taking a break from and noticed I was already doing most of what you suggested!  Cool!  It's always nice to discover you're doing things the "right" way.  I did make a few tweaks here and there (mostly based on the elimiation of header file dependencies).

Another thing: Anyone doing any serious C++ coding should really learn the STL inside and out.  I'm only now finally getting the hang of the simpler templates (such as vector, stack, basic_string, etc).  For some reason I've been staying away from the template stuff for a long long time.  I think because the syntax can be so awful.

Anyway, just today I was messing with something and converted a hand-rolled dynamic stack to an STL stack and performance almost doubled.  That was one of the coolest optimizations I've ever done in a single day.  :-)

In short, cool article.  I love this kind of stuff.  I want to see more.  I'd really like to see something in-depth about STL and other C++ performance tips and design tricks  (or should that be the other way around?) show up here as well.

"We're all patients, there are no doctors, our meds ran out a long time ago and nobody loves us." - skyknight

I love the STL (4.00 / 1) (#33)
by j1mmy on Fri May 10, 2002 at 09:22:53 AM EST

One of the larger applications I work on for my job used all manner of stacks, queues and lists, all hand-rolled. I replaced one of the most frequently used ones with an STL list, used STL algorithms (rather than rolling my own loops) to work with it, and runtimes halved or better. For an app that can run for hours and even days, it's a big improvement.

I saw the original of this article the other day and spent the entire next day at work reading the site and reading through the GotW archive.

[ Parent ]

STL (3.00 / 2) (#52)
by Peaker on Fri May 10, 2002 at 11:20:51 AM EST

lacks some pretty basic convinience methods.

If I recall correctly, std::stack lacks a pop() method that actually returns the popped object, so it requires splitting code to procedural calls to inspecting the top(), and then pop()ing. I find it resulting in much uglier code than the easily-possible functional alternative.

std::basic_string doesn't even have a string or regexp replacing facility, which makes it extremely inconvinient to work on strings.

STL lacks polymorphic wrappers to containers or iterators such that one can use generic containers in one's code. For example, with STL classes, I cannot write a base class with a virtual method that takes an iterator to any container type. It cannot be a template because it is a virtual method and it cannot be polymorphic because the STL doesn't support it.

STL's algorithms are lacking a lot of algorithms. The most obvious one from what I looked at is an O(N*log(N)) sort. It seemed that all the sort methods I could see in there were O(N*N). Also, the "N'th item" in O(N) time algorithm couldn't be found there.

The standard C++ library streams architecture is terrible. Its terribly hard to use and extend (compared to Qt streams, for example). It is very limited and unflexible, and requires total redesign. Unfortunatly, C++ is already tied by strings of backwards compatability and is now locked with no real possibility of improving its standard libraries.

[ Parent ]

a couple points: (none / 0) (#57)
by ucblockhead on Fri May 10, 2002 at 11:57:57 AM EST

Your stack problem solved:
template <class T> class mystack : public stack<T> {
public:
  T mypop() { T t = top(); pop(); return t; }
};

Regarding sort: if C++ STL standard demands that the sort algorithms be O(NLog(N)), so if they are not, you need to complain to your compiler vendor.
-----------------------
This is k5. We're all tools - duxup
[ Parent ]

bah (none / 0) (#60)
by j1mmy on Fri May 10, 2002 at 12:26:08 PM EST

If you need a pop() method that badly, you can either subclass stack or just edit the STL headers yourself.

I was also under the impression that iterators do have some base classes (forward_iterator, reverse_iterator, etc.).

STL's algorithms take the bare-bones approach. You can build more complex things from them. As for a more efficient sort, write it yourself =)

I'll agree with the streams though. I still think printf is the ultimate text i/o facility.

[ Parent ]

iterator (3.50 / 2) (#61)
by ucblockhead on Fri May 10, 2002 at 12:51:19 PM EST

No, because the containers are templates, each iterator is its own type. In other words map<string>::iterator is a completely different type from map<int>::iterator. The two types have no relation at all.

It is because that the STL is a fundamentally non-OO thing. It is generic programming, not OO programming, which OO types dislike.

There are plenty of OO container libraries around, but none of them are blessed with the "standard" seal of approval.

This is one of those places where the standards guys chose performance over convenience. In order to do what the poster wants, the increment and decrement methods on an iterator would have to be virtual, which means that they'd no longer devolve to a simple pointer increment and decrement on simpler containers like vectors.
-----------------------
This is k5. We're all tools - duxup
[ Parent ]

oic (n/t) (1.00 / 1) (#72)
by j1mmy on Fri May 10, 2002 at 04:03:27 PM EST



[ Parent ]
err (1.00 / 1) (#111)
by nevauene on Sun May 12, 2002 at 05:27:22 PM EST

If you need a pop() method that badly

Yeah, that's sort of the whole point of a stack. Push, pop...


There is no K5 Cabal.
[ Parent ]
Why pop() doesn't return a value (4.00 / 2) (#91)
by GoingWare on Fri May 10, 2002 at 10:41:59 PM EST

Herb Sutter explains in his book Exceptional C++ that pop doesn't return a value because it cannot be made exception safe to do that.

In general it is very difficult to write exception safe templates because you cannot know ahead of time when a template parameter might throw an exception. In general, a template parameter might throw an exception any time you call one of its functions, including its constructors and operators.

Herb says that in general functions that do more than one thing cannot be made exception safe.

He should know - he is the secretary of the ISO C++ Standards committee.


I am the K5 user now known as MichaelCrawford. I am not my corporation.


[ Parent ]

Wrong, wrong, wrong, I'm afraid (4.00 / 1) (#140)
by Anonymous Brave Guy on Fri May 17, 2002 at 08:28:38 AM EST

If I recall correctly, std::stack lacks a pop() method that actually returns the popped object, so it requires splitting code to procedural calls to inspecting the top(), and then pop()ing. I find it resulting in much uglier code than the easily-possible functional alternative.

The functional alternative is not possible at all with any reasonable exception-safety guarantee. The stack and such were implemented as they were for a very good reason. Smart-alecs can do tricks with derived types (which is a bad idea in itself as the library is set up), but they're just working around a limitation that was carefully put there to stop them shooting themselves in the foot. I bet the people who write such workarounds are the same ones who complain about safety in C++...

As for regular expressions, see the Boost library, which fills many obvious gaps in the current C++ standard library. Ditto for various generic container tricks, and so on. You are not the first person to think of these problems, and they have been solved already.

Your complaint about sort is totally unjustified. The usual variation in most STL implementations today is introsort, which is a quicksort variant that is usually (very nearly) as fast, but also has much better worst-case performance than quicksort.

I agree about the streams complaint, though. I couldn't have come up with a more obfuscated architecture unless I had a masters degree in Designing Java I/O Streams. ;-) The goals of increased extensibility over printf are laudable, but unfortunately, they're useless for basic internationalised applications, and there's just no excuse for the obscure names used for methods you're supposed to know how to override if you want to create different types of stream yourself. Still, we've got to leave the in-crowd something to be expert at so they can pick up their consulting fees, haven't we? :-)



[ Parent ]
Be very careful with STL. (3.33 / 3) (#64)
by jforan on Fri May 10, 2002 at 02:17:13 PM EST

It is not all implemented exactly the same, and that can expose bugs that are very difficult to track. Additionally, when debugging, many of the STL implementations are written very poorly.  

My experience of S-T-HelL included a multi-threaded server program I was debugging, where various team members (including myself) assumed that the "STL" string was thread safe for read operations.  (I was not the one who recommended the use of STL, nor was I the person who layed out the global variable system we used.)  Because string does copy-on-write and consequent ref counting, and because the library I was using used the ++ operator to do the refcounting, the ref count on one of the thousands of strings would eventually (over ten minutes of heavy load) go to zero, and the string would be deleted (while still in use).  Of course, the code that implemented the STL library I was using was so obfuscated I basically had to rewrite the whole string class in the last hours of development and change the implementation to use a thread-protected ref-count incrementor/decrementor.

Anyway, in crucial and/or time-critical server-side development (which is more than half of the coding I do) I no longer use any code that I don't know exactly how it works AND is implemented cleanly, unless someone else will take responsibility for it.  And even then, unless that person is intelligent and wrote the code themselves, I am always very leary.

Jeff
I hops to be barley workin'.
[ Parent ]

Reference counted strings (2.00 / 1) (#129)
by mrbkap on Tue May 14, 2002 at 12:06:38 AM EST

Unless you *really* need ref-counting, most implementations I know of (Dinkumware's for instance) have switches/defines to turn off reference counting for (I think) this very reason. In fact, many of COW's opponents cite this as a major problem with it. (Part of the problem here is that the standard doesn't even mention threads, which gives implementors a wide variety of options on their implementation).

-mrbkap
[ Parent ]
Those aren't STL flaws (2.00 / 1) (#139)
by Anonymous Brave Guy on Fri May 17, 2002 at 08:02:17 AM EST

I agree that the STL can suffer from all of the problems you describe, but that's different from saying it has to.

Yes, many STL implementations suck for debugging, no two ways about it. That's hardly the STL's fault, though, and those who implement it have finally twigged that names like _FI are not helpful.

If you assumed that the STL string was thread-safe for reads, you shouldn't have. Nowhere in the C++ spec does it make any promises about thread safety. If you made a bad assumption instead of doing the basic homework, it's unfair to blame the STL for it.

Similarly, you claim that the C++ string type does copy-on-write. This, again, is not required, and I believe the prevailing opinion among C++ experts is that in practice, COW is a serious liability and gains little. I know of no good STL implementation that still uses any sort of COW technique for its strings.



[ Parent ]
Correction (none / 0) (#144)
by jforan on Wed Jun 19, 2002 at 04:20:48 PM EST

Because string does -> Because string did.

You are correct in some sense, but I would say that the lack of specification is a flaw.  Interfaces imply certain functionality, and development requirements often necessitate the skimming of specifications.  I am doubtful that most STL users are intimately familiar with its specifications.  Something that is that widely used ought to be better specified, enabling for quicker learning and adoption.

No one was arguing about specific implementations of STL before I wrote my comment.  I was pointing out that using STL can be dangerous because its specification is poor.  I would say flawed.

Jeff

I hops to be barley workin'.
[ Parent ]

God I hate the cult of C++ (3.62 / 16) (#9)
by eLuddite on Thu May 09, 2002 at 11:06:37 PM EST

Many other languages are simpler to learn in part because they limit one's choice.

They are simpler because they are *cleaner*.

Should a class use inheritance or composition? Will inlining this function really improve performance? Shall I encourage reuse through polymorphism or templating?

These are not useful choices. These are unavoidable decisions that interfere with the process of solving a problem without contributing to the problem's solution. Having to play Mr. Compiler is not a useful "choice". Nowadays we use computers to do the tedious grunt work so that we dont have to. Apparently, everyone except C/C++ programmers understands the value of computers. It's funny because it's true.

If C++ had useful choices, C++ would offer more than one programmming idiom. Python is an example of a programming language which offers many choices: list programming, functional programming, true object oriented programming, and programming as if all the world were C/C++.

People, if you dont know C/C++, dont learn it! If you learn it, you will spend the rest of your life programming in C/C++ regardless of the language before you.

---
God hates human rights.

God, I hate language bigotry (4.75 / 4) (#16)
by benDOTc on Fri May 10, 2002 at 03:15:58 AM EST

Many other languages are simpler to learn in part because they limit one's choice.

Lets get one thing straight:  When a C++ programmer says that other languages limit your choices, what she really means is that other languages (many, at least) don't support multiple inheritance.  This is a bit of an oversimplification, but it's the big point.  Many, many OO programming languages don't support multiple inheritance because it leads to some messy situations, but then spend a lot of time making up for the lack of it.  Java interfaces are a good example where for each class with the interface, the programmer is required to rewrite the methods, when multiple inheritance might work fine.  Of course, MI can be really messy and lead to badly designed code, but the fact that it's available in C++ is (in the minds of many/most C++ programmers) a good thing.

If C++ had useful choices, C++ would offer more than one programmming idiom.

C++ is certainly not all things to all people, but faulting it for being too rigid is faulting most programming languages, as C++ is really rather flexable in this matter.

People, if you dont know C/C++, dont learn it! If you learn it, you will spend the rest of your life programming in C/C++ regardless of the language before you.

Distrust anyone who considers knowledge in and of itself to be dangerous.  Learn as many programming paradigms as possible (that doesn't just mean learning the syntax, either!).  It won't hurt.

ben.c


[ Parent ]

I recommend learning many languages (4.33 / 3) (#18)
by GoingWare on Fri May 10, 2002 at 03:46:47 AM EST

While I feel C++ is the best choice for me, in my consulting work I advertise support for C, C++, Java, Smalltalk, Postscript, PowerPC and 680x0 assembler, and Python.

In the past I also programmed in FORTRAN, Pascal and Lisp. I wrote a pretty good chunk of a common lisp interpreter - Star Sapphire Common Lisp is now available as freeware and has the unique property that you can get a full common lisp environment with room for your applications and microemacs to run on a 640 kilobyte PC/XT box. It does this through a software-implemented virtual memory where we had to manually put things into and get things from the VM store.

I've also done a lot of emacs lisp programming.

While I feel C++ is the most versatile programming language, the best programmers are those who have learned enough languages that they understand the relative strengths and weakness of each.

The lack of multiple inheritance doesn't bother me so much in other languages. One thing that does is having to allocate everything from the heap, it is difficult to make extremely efficient code when it is all allocated.


I am the K5 user now known as MichaelCrawford. I am not my corporation.


[ Parent ]

Inheritance. (4.50 / 4) (#21)
by katie on Fri May 10, 2002 at 04:20:29 AM EST

The usual argument for not having multiple implementation inheritance is that single-impl-inheritence plus multiple-interface-inheritance is "sufficient".

I contend that if it is, NO impl-inher plus delegation to multiple-interf-inher is sufficient: why pick ONE of the ancestor classes to be "special" and allow implementation inheritance from it? If you're going to make people write delegations, make them do it for everything.

Oh, and then fix the bloody tools to manage all that sensibly...

Sufficient != convenient.

And I've seen enough single-inheritance flailing around to understand that restricting MI isn't the solution. MI is not the problem. The problem is that many people find inheritance complicated at all.

It's like the "no operator overloading". It makes matrix and vector operations look REALLY ugly. And the argument "people do stupid things with the overloadings" is a red herring - people go naming functions "DoTheThings", do we stop them picking function names?

C++ is not an especially hard language. Trying to make it simpler so more people can use it is not enhancing software development. Dumb people will write broken software in C++, Java or VB. It doesn't matter how dumb you make these languages, people will write stupid things in them. The coding is NEVER, EVER, EVER the hard part of creating software. It's a tiny, tiny fraction of the effort involved. The hard part is working out what to write - requirements gathering, designing systems, planning deployments. The compromises and conversations - not doing the writing.

Faffing with the languages is like swapping pens for pencils to see if makes joe public any better of a novelist. A great novelist can write a great novel in wax crayon: the tools don't matter.

[ Parent ]

VB (5.00 / 2) (#46)
by pinkcress on Fri May 10, 2002 at 10:57:05 AM EST

Dumb people will write broken software in C++, Java or VB.

VB is magical. It'll can make even clever people write awful, broken code, no matter how hard they try :)

[ Parent ]

C++ deserves a lot worse than language bigotry (3.36 / 11) (#23)
by eLuddite on Fri May 10, 2002 at 04:49:29 AM EST

However, I dont mean to be as critical of the language as I do of its undeserved pervasiveness and status in the software industry. It's not about the language, per se, it's about the self-perpetuating pointless and counterproductive overuse of the language; it really is time to stop abusing the tits on the sacred cow.

When a C++ programmer says that other languages limit your choices, what she really means is that other languages (many, at least) don't support multiple inheritance.

When I say "I was not aware of that," what I really mean is I wasnt aware that C++ programmers made such stupid and contrived comments in their defense. Sorry, multiple inheritence is not a reason to use C++ unless all you know is C++ and interpreted C++ (Java.) Multiple inheritence is a feature of almost all oo languages.

It won't hurt.

Heh, you've never seen non-C++ code written by C++ programmers; they just cant seem to disinfect themselves from that language's semantics and idiomatic style. It is counter-productive to induce one's own brain damage by prematurely learning C++. It's very hard to merely "learn" C++ when it practically demands your complete immersion into a culture of libraries and make-work diversions such as smart pointers. If they're so smart why arent they a transparent part of the language?

No, C++ should be considered mind-alteringly harmful. It's a terrorist language.

---
God hates human rights.
[ Parent ]

Hey, I'll say it (none / 0) (#83)
by rho on Fri May 10, 2002 at 08:03:14 PM EST

I can't generate enough interest to care how the computer stores, retrieves or counts pointers, memory, files or any other bit-flipping chore. This is not a new issue with me.

It was something to be concerned about 10 years ago. It isn't so much, now, except in highly specialized cases.

I care about ad-hoc creations, and how fast I can put them together. I care about looking at somebody's handy class/function and figuring out that it will apply in my situation. Mostly I care about getting a specific job done quickly and easily. This is usually not satisfied by C++, not that I could program C++ if my mother's life depended on it.

That being said, I sure did like this article. It had a few nuggets of wisdom that I can use even in my normal PHP tasks. Clever programmers and clever programming benefits everybody.
"The thought of two thousand people munching celery at the same time [horrifies] me." --G.B. Shaw
[ Parent ]

God, I hate language bigotry (none / 0) (#142)
by Homburg on Mon May 20, 2002 at 05:59:51 PM EST

Lets get one thing straight: When a C++ programmer says that other languages limit your choices, what she really means is that other languages (many, at least) don't support multiple inheritance.
Or generic programming. Or functional programming (which is rather well supported by the STL, particularly if you add boost's lambda library). Or value classes vs. entity classes (I'm thinking of Java and Objective C here). There are a hell of a lot of options C++ gives you which other mainstream languages don't. (Dylan and Eiffel do have much of the flexibity of C++ and are cleaner, but are unfortunately nowhere near as popular).

[ Parent ]
Languages (5.00 / 3) (#47)
by ucblockhead on Fri May 10, 2002 at 10:59:30 AM EST

I've noticed an odd thing here at k5...the C++ guys write articles about C++. The non-C++ guys just bitch about how C++ sucks. They rarely seem to write articles of their own.

Why is that?
-----------------------
This is k5. We're all tools - duxup
[ Parent ]

simple ! (4.60 / 5) (#53)
by oliv on Fri May 10, 2002 at 11:20:58 AM EST

It must be that with a well-designed language you don't need articles teaching you how to such fundamental things as arguments passing ...

[ Parent ]
Yeah....right.... (4.00 / 1) (#58)
by ucblockhead on Fri May 10, 2002 at 11:59:17 AM EST

Obviously no one ever needed to learn how to do functional programming.
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
you should think of it as tough love (5.00 / 1) (#70)
by eLuddite on Fri May 10, 2002 at 03:17:49 PM EST

They rarely seem to write articles of their own.

What were you doing while the internet was filling up with sex talk and articles on computing? Cramming for your C++ Lawyer finals?

---
God hates human rights.
[ Parent ]

Programming Idioms (5.00 / 1) (#54)
by lovelace on Fri May 10, 2002 at 11:25:53 AM EST

If C++ had useful choices, C++ would offer more than one programmming idiom. Python is an example of a programming language which offers many choices: list programming, functional programming, true object oriented programming, and programming as if all the world were C/C++.
C++ most certainly offers "more than one programming idiom". You can do "list programming, functional programming, true object oriented programming" and more. (Procedural, generic...). See Multi-Pardigm Design in C++ for a good discussion of this. Python is a good language too, but don't disparage C++ just because you don't understand it.

[ Parent ]
"I can do all that stuff on a turing machine! (none / 0) (#69)
by eLuddite on Fri May 10, 2002 at 03:10:32 PM EST

Or psuedo-code for that matter.

You get a clue. The paradigm you describe is called "playing Mr. Compiler for class credit." Outside its limited pedagogic value (the quip about psuedo-code refers), I'd rather indulge the class of problems collectively known as "Exercise n, Chapter m" by writing 10 simple and correct lines in the appropriate language; spending the first m-1 chapters developing libraries in order to accomplish the same thing badly in C++ is not an option, it is a curiosity.

1. Read and understand trivial problem.
2. Write compiler for subset of language X in C++.
3. solve 1 using 2.
4. Profit!

---
God hates human rights.
[ Parent ]

Choices (none / 0) (#109)
by ustenzel on Sun May 12, 2002 at 03:16:18 PM EST

Should a class use inheritance or composition? Will inlining this function really improve performance? Shall I encourage reuse through polymorphism or templating?

These are not useful choices. These are unavoidable decisions that interfere with the process of solving a problem without contributing to the problem's solution.

Actually these choices are part of the problem. Do these objects have a is-a or a contains-a relationship? Is this problem solved by runtime or compiletime polymorphism? You eventually have to answer these questions, then express them in the programming language of choice. The only complication is that some languages just cannot express some concepts. I'm completely at loss trying to find another language besides C++ that provides compiletime polymorphism.



[ Parent ]
C++'s a nice language... (3.60 / 5) (#10)
by MSBob on Fri May 10, 2002 at 12:19:07 AM EST

when coupled with a good library like QT for example. It is a more dificult language than most but it's quite rewarding too because of the number of options you get. for many apps (such as serious OpenGL stuff) it is the obvious choice.

I did C++ for more than three years professionaly (and another two years as a hobby prior to graduating) and have been doing Java for the last two years. My move to java wasn't by choice but dictated by market forces. Where I'm based there isn't a single C++ oriented company within a 100 mile radius.

Having said that I enjoy doing java. What I really like about java is reflection. I know it is hated with passion by some Smalltalk buffs but reflection is a nice practical api for dynamic type discovery. There are just so many possibilities with reflection for writing 'smart' programs that can introspect their own structure. C++ equivalent is the typeid operator which is not nearly as advanced as java's reflection. Not that you cannot do any of that stuff in C++ it's just that nobody (so far) has managed to put together a class library for dynamic type discovery. When designing things like persistence layers reflection can cut down the amount of code by a significant margin and the new (as of JDK 1.3) dynamic proxy thing just kicks ass! I wonder whether anyone has thought of something equivalent in C++. I think there may be a way to do it with function pointers but it certainly would be very hard given the static nature of C++ linkage.

I'm not sure how I'm going to feel about C++ once I go back to it again. There are many things in java that I would miss. On the other hand java does have many practical issues that just can't be ignored (such as the superannoying to the end user garbage collector).

I hope C++ becomes a little more dynamic with time so I can have the benefits of a dynamically typed language with the speed of C++ and without the garbage collector.

I don't mind paying taxes, they buy me civilization.

Gartbage collectors, memory leaks and pointer bugs (4.75 / 4) (#12)
by GoingWare on Fri May 10, 2002 at 12:33:54 AM EST

It is interesting to see someone who appreciates java disparage the garbage collector.

It is possible to have better garbage collectors. I have seen a demonstration of a real-time control system built with java that could float a ball in the air magnetically, and even oscillate it up and down at the user's command. It used a native compiled java, a proprietary "ahead of time" compiler kind of like gcj.

But one of my strongest beefs with Java is that it is commonly hyped as being free of memory leaks and stale pointer errors. These are simply not true.

If you have an error in your program that leaves an unwanted reference to some memory around, it will not be garbage collected.

Java programs abound with memory leaks, and I have seen it in action in Java web server applications, where the server eventually ground to a halt as it ran out of swap space and we had to restart it periodically to free up all the memory.

Consider holding a reference to any node of a Document Object Model tree. DOM nodes hold references to both their parents and children, so holding a reference to a single node can keep a 20 megabyte DOM tree lying around in memory!

Similarly, failing to set a reference to unwanted memory to null will allow the program to use it without crashing. Many people claim that this frees the program of nasty bugs but I assert it is a worse bug than having a dangling pointer. With proper testing, a dangling C++ pointer will show itself vividly, while leaving an unwanted reference in a valid state can cause subtle errors in behaviour that are very hard to debug.


I am the K5 user now known as MichaelCrawford. I am not my corporation.


[ Parent ]

Java memleaks considered not harmful (4.00 / 2) (#19)
by obsidian head on Fri May 10, 2002 at 04:00:16 AM EST

If you have a tool that can give you the entire call reference graph of all objects, you can look at what object's multiplying like mad, and see what down the line should have dropped the reference.

But I actually agree with you strongly; most people don't know how to fix this, and only Sitraka's JProbe is the common tool to diagnose.  Java memleaks are extremely common.

[ Parent ]

OptimizeIt's supposed to help also (3.00 / 1) (#20)
by GoingWare on Fri May 10, 2002 at 04:11:51 AM EST

OptimizeIt says that it helps with memory leaks, although I haven't tried it myself.

I customarily link to the OptimizeIt web page when I make the point that Java really does leak.

It was recently acquired by Borland.


I am the K5 user now known as MichaelCrawford. I am not my corporation.


[ Parent ]

True (4.00 / 1) (#26)
by delmoi on Fri May 10, 2002 at 07:23:47 AM EST

I once had a memleak in a custom program I wrote that would display jpgs. The problem was that the entire decompressed image would 'leak'

It was driving me insane trying to find the problem and I really wished I could have just called 'delete'. (It turned out I had to call clear() or something on the vector that was holding 'em or something... just calling 'remove()' on the individual items didn't do anything)
--
"'argumentation' is not a word, idiot." -- thelizman
[ Parent ]
Part of STL spec? (4.00 / 1) (#30)
by MSBob on Fri May 10, 2002 at 08:26:09 AM EST

Isn't your "bug" a part of the STL specification? As I remember you call remove which basically reshuffles your elements so that the 'redudndant' stuff is as the end and then you need to call erase() explicitly.
I don't mind paying taxes, they buy me civilization.

[ Parent ]
Pools (4.00 / 1) (#88)
by GoingWare on Fri May 10, 2002 at 09:35:23 PM EST

That's true.

Also a suprising aspect to STL that appears to cause memory leak is that the allocators are often implemented in terms of pools. This allows more efficient allocation and deallocation, but I'm not sure there's a way to shrink the pool. The only alternative would be to provide your own allocator.


I am the K5 user now known as MichaelCrawford. I am not my corporation.


[ Parent ]

Ignorance (4.20 / 5) (#27)
by hoggy on Fri May 10, 2002 at 07:53:01 AM EST

But one of my strongest beefs with Java is that it is commonly hyped as being free of memory leaks and stale pointer errors. These are simply not true.

I think the problem is often that people fundamentally don't understand garbage collectors. Programmers often seem to think that because you have a garbage collector you don't need to think or care about memory.

I think the problem is that most programmers haven't learned any computer science.  I can't think of any other serious profession where self-taught people can get let loose on real systems. You can't cut people open unless you're a trained surgeon, no-one sells "Learn bridge design in 21 days" books. Why is Software Engineering such a mess?

I'm not being elitist here. I don't think programmers without degrees are bad or stupid, I just think most programmers are unaware of basic computing science theory.

Great article, but a bit sad that it should be needed. Do Doctors websites have articles entitled "Top tips for not killing your patients"? "No.1: don't leave your kit inside their body when you sew them back up"...


[ Parent ]

Self-taught programmers (4.00 / 1) (#51)
by Peaker on Fri May 10, 2002 at 11:11:24 AM EST

Are usually ones that take interest in what they do, and take the time to understand computer science involved.

Most self-taught programmers I know are aware of algorithmic efficiency concerns and don't overuse memory or unoptimized algorithms.

[ Parent ]

I'm a self taught programmer (educated in Physics) (5.00 / 2) (#87)
by GoingWare on Fri May 10, 2002 at 09:33:18 PM EST

I'm a self-taught programmer. I dropped out of college in my junior year to take a programming job before going back after 6 years and completing my degree - in Physics.

However, I recognized early on the disadvantage I had in the job market compared to programmers with CS degrees, and studied hard. I programmed on a Mac 512k at night, and I read Knuth's "Art of Computer Programming" on the bus on the way to work.

I actually had one real computer science class, CS 10 at CalTech, which covered basic data structures and algorithms. It's the foundation of my career.

I meant what I said at the end of the article, about how one should always buy books. That's how I've been able to do it.


I am the K5 user now known as MichaelCrawford. I am not my corporation.


[ Parent ]

Books (4.00 / 1) (#93)
by Peaker on Sat May 11, 2002 at 09:48:34 AM EST

I haven't read a lot of books about computer science, but I've read a lot of meterial on the web, and small bits from books too.

I think I definitely could know about Red black trees, AVL trees, heaps, fibonacci heaps, complexity analyzing techniques/etc without books. In fact, I've learned none of those from a book, but from websites and interested friends.

[ Parent ]

TAoCP (none / 0) (#112)
by Alfie on Sun May 12, 2002 at 09:45:46 PM EST

Knuth's The Art of Computer Programming series is one set of books that every serious programmer should own. I would venture that they are capable of replacing professors and university courses for any determined learner.

Basically, Donald Knuth has rigorously detailed the process of learning how to program a computer without detracting from the joy of the experience. He is one of my personal heros.



[ Parent ]
Java and garbage collection (4.66 / 3) (#44)
by Simon Kinahan on Fri May 10, 2002 at 10:53:39 AM EST

I'm always surprised when people talk about having to null out variables in Java. My immediate take on it is that if you need to do that you're not writing in good OO style. Such style is perhaps only debatably desirable, but it really is the way in which Java is intended to be used.

Why does it help ? Well, a good OO program should have short methods (20 lines is a good goal) and smallish classes (5 or 6 significant methods). Methods should not run on from doing one thing to doing another. Classes should not contain multiple bundles of unrelated data. If you program like this, you'll find the need to null out variables is rare, simply because things go out of scope quickly in short methods, and small classes will usually have well-defined lifetimes without multiple phases in which they do several different things.

Yes, you do need to plan the lifetimes of your objects, but this is just good design. Its not a separate activity. I'm not just sounding off here: I really do program in the style I'm talking about, and I really don't get memory leaks. Honest. And you really can't get stale pointer errors in a GC'd language, because if the pointer is still there, its not stale.

You can get null pointer errors, but they're pretty easily avoided by sensible design: if certain methods can only be called at certain times, it needs to be documenet, and so on. Not writing in a style that requires you to go around nulling things out helps as well, of course. The thing people sometimes do, and which I do object to, is to use null to indicate some state of the system. Its much better, if you feel the urge to do this, to create a null subclass of whatever interface you're talking to, and use that instead. That way you can call it without anything crashing, and you don't have to remember that it might be null.

As for "better" garbage collectors: I suspect you're talking about a real-time collector. They're good for hard realtime systems, because new is guaranteed to return within a bounded period of time, and there are no pauses, however more time is spent collecting garbage in total than would be spent using the usual generational scheme.

Simon

If you disagree, post, don't moderate
[ Parent ]

Trees (5.00 / 1) (#48)
by Peaker on Fri May 10, 2002 at 11:03:42 AM EST

I program in the style you mentioned, and I (almost) never have to 'null out' references. And yet, it happened to me at least once, that I held an object I deemed useful (and it was, for later), and forgot that it is part of a tree of objects, and actually referenecs the entirety of the tree. This led to a memory leak. Memory leaks are still possible with this style.

Sure, I should have extracted the relevant information or node from the tree, but that's non-trivial.

[ Parent ]

Yes (none / 0) (#49)
by Simon Kinahan on Fri May 10, 2002 at 11:08:41 AM EST

It happens to me too, from time to time, it just doesn't weigh very heavily on my mind, in the manner that some posters imply it does on theirs.

Simon

If you disagree, post, don't moderate
[ Parent ]
Java memory leaks (3.00 / 1) (#90)
by swr on Fri May 10, 2002 at 10:07:20 PM EST

If you have an error in your program that leaves an unwanted reference to some memory around, it will not be garbage collected.

In my experience with my own code, the most common case of memory leaks is using one of the Collections objects to maintain some state within a long-living object. Adding an item to the collection, then failing to remove it when it is no longer needed. Within such long-living objects it can be helpful to regularly log the size of its collections with println or whatever logging library (only when the program has a debug flag active, of course). Not only does this allow you to see if the collections are growing out of control, it can also give information about the system's state, which can reveal bugs you hadn't noticed yet.

One way to avoid memory leaks, in some cases, is with weak references. See java.lang.ref, and java.util.WeakHashMap. Alas, there is no corresponding WeakHashSet, and WeakHashMap.keySet() returns an unmodifiable Set. Of course, you can create your own WeakHashSet, or use WeakHashMap with keys and no values... A WeakHashSet would be useful for storing the set of listeners/observers/etc, so that if some observer object is no longer in use the observable won't prevent it from being GC'ed.



[ Parent ]
garbage collecting (3.50 / 2) (#25)
by martingale on Fri May 10, 2002 at 06:33:14 AM EST

The existence of a garbage collector in Java shouldn't be taken as a license to instantiate. In fact, I'd suggest that the problem with most Java programs is that the programmers don't know about/bother with memory pools. Design your memory usage before implementing, and call the garbage collector yourself at strategic moments. It improved my Java projects a lot.

[ Parent ]

smalltalkers hate reflection? (4.00 / 1) (#65)
by radeex on Fri May 10, 2002 at 02:27:38 PM EST

Pardon me, I'm not much familiar with the Smalltalk community, but why would they hate reflection? Did I misinterpret what you were trying to say? Perhaps you meant that they hate Java's pathetic excuse for reflection, and wish there were more of it? ;-)
--
I DEMAND RECOMPENSE!
[ Parent ]
Its not reflection, its Java's reflection (5.00 / 1) (#118)
by Simon Kinahan on Mon May 13, 2002 at 07:21:40 AM EST

Many Smalltalkers regard Java the usual resentment expected of people who've gotten used to a superior technology and then seen an inferior one dominate the market for what appear to be spurious reasons. I include myself in that category :)

Java's reflection is a particular bone of contention, because where as in Smalltalk you could call an arbitrary method like this:

anObject perform: #aMethod with: anotherObject.

In Java you have to do this:

try {
    Method aMethod = anObject.getClass().getMethod("aMethod", new Class[]{anotherObject.getClass()});
    aMethod.invoke(anObject, new Object[]{anotherObject});
} catch (JustAboutEveryExceptionEver ex) {
    ... Not much sensible you can do here
}

Smalltalkers think this is silly and inelegant, which it is, because Java's reflection is not designed to be used the way Smalltalk's is. Smalltalk doesn't have any static typing at all, or any lightweight class syntax, so the usual way to link to loosely couple classes is to tell the caller what method to call on the callee. In Java the same design patterns are accomplished by having the callee pass the caller an object that implements an interface.

Java only makes use of reflection in extreme cases where the caller knows nothing about the callee at all (such as Beans, or serialization). In those cases the Java Way makes more sense, because you typically need to investigate and select the methods you're going to call, and then call them a lot, so having the separate Method class is useful.

Simon

If you disagree, post, don't moderate
[ Parent ]

Ah, about the poll . . . (4.66 / 6) (#13)
by tmoertel on Fri May 10, 2002 at 12:42:34 AM EST

First, good job on your re-write of the story. (It earned my +1.)

The poll, however, is fundamentally flawed. The notion that a particular programming language is what allows good programmers to be their best ignores the reality that programming languages are tools.

What hammer can the best carpenters build the best carpentry with? Is it the 20-ounce rip claw hammer? The 24-ounce jacketed graphite plain face hammer? Or perhaps the lightweight 16-ounce antivibe rip claw? Well, which is it?

The answer, of course, depends on the carpenter, the job, and the other tools available.

The same goes for programming. There is no "best" language. All programming languages are tools. As with carpentry tools, some are easier to use than others, some are applicable to a wider range of problems than others, and some give you more benefits in certain specialized domains than do others. But none of them is categorically better than the rest.

To imply that a programming language can allow good programmers to be their best puts the hammer before the both the carpenter and the job---when the reality is that the hammer is secondary to both.

--
My blog | LectroTest

[ Disagree? Reply. ]


Languages are just for expression (4.00 / 2) (#32)
by DodgyGeezer on Fri May 10, 2002 at 08:55:31 AM EST

Agreed. The language we choose is just what we use to express the implementation of our solution. Some languages make it easier to express some things, and some of them take quite a bit of re-thinking to learn. For example, an OO programmer *can* express themselves in a functional language like Scheme - the solution tends to be more object-based than truly OO though.

Saying that though, I consider myself most articulate in C++. My projects seem to be generally successful. But I've got to say, I'm sick of dealing with the crap from bad C++ programmers on some of the teams I've worked with. I'm not talking about junior people either: somebody who claims 10 years of C++ programming experience shouldn't be hiding unknown exceptions with catch(...), and a few months later arguing endlessly that double deletes are okay.



[ Parent ]
C++ pitfalls? (5.00 / 1) (#75)
by unDees on Fri May 10, 2002 at 05:59:02 PM EST

somebody who claims 10 years of C++ programming experience shouldn't be hiding unknown exceptions with catch(...)
Really? Is that always the wrong thing to do? (Not a troll--I really want to know what you mean here.) What if you're calling a horribly implemented third-party library with no exception specifications (or brain-dead ones) and a tendency to throw $DEITY-knows-what? I've run into some truly unfathomable exceptions coming from Borland-generated wrappers to IDispatch interfaces to shaky ActiveX libraries. Rather than letting my program blow up on unexpected(), I thought it better to catch as many exceptions as I reasonably could and then have a (...) block to get the ones that weren't specified.

Your account balance is $0.02; to continue receiving our quality opinions, please remit payment as soon as possible.
[ Parent ]
catch(...) (5.00 / 2) (#77)
by DodgyGeezer on Fri May 10, 2002 at 06:12:57 PM EST

That's insane: COM objects aren't allowed to throw exceptions (althoug they can). That breaks the binary compatibility of COM - not all languages can handle C++ exceptions. You've got to wonder about the state of the entire process in this situation... I hope you're not catching and hiding 0xC0000005.

In your situation, it doesn't sound like you have much choice. The situation I was referring to was all within one home-brewed C++ app. I wrote about it once before.



[ Parent ]
COM and exceptions (5.00 / 2) (#78)
by unDees on Fri May 10, 2002 at 06:26:01 PM EST

To be fair, Borland C++ Builder adds a few exceptions of its own when you generate an IDispatch wrapper. But I really do think this particular COM object I'm forced to use, and not the Borland added-on stuff, is throwing the exception in my case.

I just read your comment to the article on error handling. I agree that widespread use of catch(...) to the detriment of careful exception planning will just hide problems so that they can surface later on in the form of mysterious crashes and memory leaks.

Your account balance is $0.02; to continue receiving our quality opinions, please remit payment as soon as possible.
[ Parent ]

cliche (5.00 / 2) (#35)
by kubalaa on Fri May 10, 2002 at 09:59:03 AM EST

This is well put, but it's obvious, meaningless, and I'm sick of hearing it. Can we agree that a bad language can hurt programmer productivity? And that there is a limited amount of productivity to go around? So perhaps we can't single out a language as "the best", but we can certainly say that some languages are better than others. And we should consider the amount of resources we dedicate to supporting a language in light of these sorts of comparisons. Do we honestly think that C++ is /so/ much better than all it's alternatives that we should be spending all this time trying to tame it instead of improving something which is already a bit more manageable?

Language wars are NOT pointless.

[ Parent ]

Language wars ARE pointless (4.66 / 3) (#124)
by avdi on Mon May 13, 2002 at 02:13:22 PM EST

we can certainly say that some languages are better than others
No, we can't. I like lots of languages. C, C++, Java, Perl, Python, Ruby, Smalltalk, Haskell, Lisp... all have their place. All are good and useful tools. All are really nifty in some ways, and really annoying in others.

VB is the one language that I pretty much dislike completely. And yet I've seen people be amazingly productive with it. Hell, I've been remarkably productive in it myself, especially for someone who had zero formal education in Basic and learned it while hacking spreadsheet macros.

Language wars are a waste of time, resources, and mental energy. True cross pollination of ideas, from one language community to another, is genuinely useful. If we can talk non-defensively about the areas in which the languages we are most familiar with are strong, then everyone will benefit from a better understanding of which language to pick for a given job. Unfortunately, as long as we there are stubborn clods around who loudly insist on the "some languages are better than others" myth, these useful exchanges will be drowned in a barrage of yelling and chest-beating.

--
Now leave us, and take your fish with you. - Faramir
[ Parent ]

Why do we have languages anyway... (5.00 / 1) (#135)
by his on Wed May 15, 2002 at 12:17:40 PM EST

This is something that is bugging me for some time. Why do we have so many programming languages anyway? Can anybody tell me, what can be done in C++ that cannot be done in C, what can be in Java, that cannot be done in Eiffel? Leave the hardware aside. Why can't I programm C++ Syntax and multple inheritance and use a Java gargabe collection? Why are we just talking about languages as an closed collection of syntax, features, layout?

Take for instance comment characters in normal programming languages:

// (C, Java), # (Skript-Languages), { } (Pascal)

I'm sure everyone has a favourite in those and yet they should 100% replaceable and no language should care if I redefine them.

Why can't I build my language from scratch for the purpose I need at the moment? Build the language for the problem?

So I want strongly typed, pre- and postconditions, gargabe-collected language for half of my project and then I switch the language to be C dirty for the speed hacks in the inner loop. Next I switch on Script-Mode in my command-line parsing function and enjoy regular expression and strong string function, while I don't have to fear being bogged down with a speed penalty in the rest of my code.

Admit it, writing cross language code is difficult, and really nobody does it, if not forced.

[ Parent ]

.NET (none / 0) (#136)
by avdi on Wed May 15, 2002 at 12:55:20 PM EST

This seems to be precisely what MS is getting at with .NET - computer languages are basicly the same, so why not let them all compile to a common platform, and make them all able to talk to each other, and all able to leverage the same common set of libraries?  Let the programmer choose the most applicable syntax for the task at hand.  Of course, a lot of programmers are in a snit over this - they are very upset that someone would suggest that their pet language shares so much in common with those other, lesser languages.  Some have written whiny columns about how .NET's multi-language support boils down to having various syntax "veneers" over the same core language - something that, even if it were true, wouldn't seem to me to be such a bad thing. The fact of the matter is that 90% of popular languages are 90% the same, and it's about bloody time somebody pointed out that fact.  While we don't need a One True Language, we do need environments that allow us to use multiple languages in a single project, without having to write our own glue code for every single language junction.

--
Now leave us, and take your fish with you. - Faramir
[ Parent ]
The whiners are (partly) right (none / 0) (#145)
by tpv on Tue Jul 16, 2002 at 04:26:40 AM EST

.NET is a step in that direction, but it doesn't give the parent poster, what they asked for.
e.g. You can't write a .NET program without garbage collection. And you can't write one with fully support multiple inheritance.

.NET is a good step. And they're working on fixing some of these shortcomings, but we don't have true mix-and-match yet, and I don't think .NET will ever deliver it.

[ Parent ]

Yes, but... (5.00 / 6) (#36)
by ghjm on Fri May 10, 2002 at 10:00:10 AM EST

Suppose you lived in a world where most of the carpenters were using a 5-pound sledge for almost all of their tasks. You are one of the few people who have tried a lightweight or antivibe hammer, and you've found it to be excellent for detail work.

Whenever you try to tell people about how cool your little hammer is, the other carpenters generally laugh at you, because obviously the 5-pound sledge is a better hammer, because it's heavier. You can't expect a carpenter to do serious, professional work with a hammer that weights less than a pound! You must be some kind of amateur.

Meanwhile, the vast majority of cabinetry is being delivered with obvious breaks and cracks, because most carpenters are still pounding on it with sledgehammers. There is much discussion of this problem, and the industry seems to be consolidating around a solution: Instead of actually using the sledgehammer to drive nails, lay it flat against the nail, then tap the other side of the sledge with a screwdriver until the nail goes in.

This seems ludicrous to you, because you see it as obvious that the problem is using a sledgehammer in the first place. Your answer is to use a lightweight hammer, but whenever you try to talk about it with "sledgehammer people," somehow the discussion always winds up getting lost in a debate on the merits of various sledgehammers.

Might you not become quite shrill at this point?

-Graham

[ Parent ]

And your point is? (2.50 / 2) (#39)
by AndrewH on Fri May 10, 2002 at 10:41:54 AM EST


John Wilkes Booth, Lee Harvey Oswald, John Hinckley Jr — where are you now that we need you?
[ Parent ]
My point (none / 0) (#76)
by ghjm on Fri May 10, 2002 at 06:02:23 PM EST

Oh pointy birds,
Oh pointy pointy.
Anoint my head,
Anointy nointy.

[ Parent ]
Ironic (4.00 / 2) (#15)
by streetliar on Fri May 10, 2002 at 03:10:25 AM EST

I was the loudest critic of the old story, so I am glad I had the chance to place that last vote that took this story to the front page. A much nicer story, much more satisfying and meaty.

Thank you very much (4.62 / 8) (#17)
by GoingWare on Fri May 10, 2002 at 03:36:09 AM EST

I want to say a word of thanks to all who supported both this and the first draft, and to all who commented in moderation.

Some of the comments were difficult to read. You're a tough crowd to please. But for the most part I agreed with them and so the draft that actually got posted is much improved. Even where I didn't agree with a criticism it helped me to clarify my own thinking and support some of my controversial arguments much better in the new draft.

It was hardest to read the comment that called me an "old school elite geek", as I have been convinced for some time I am experiencing age discrimination in my search for new work. Having fifteen years experience doesn't make me employable, it makes me "overqualified", as I was informed by one company after I inquired why I wouldn't be getting an interview for a job I wanted badly.

But upon some reflection, I've decided that old school is the right way to be, and I'm proud of it.

Some people suggested in response to my mention of this in my diary that I try going into embedded or game console work, where efficiency and reliability are prized much more than with the desktop programming I usually do. And in fact I have been studying embedded programming for a little while, as well as Linux kernel programming.

I hadn't considered games, but I think I would enjoy it.

A friend who was very influential early in my career is an old Atari 2600 programmer, a platform with 128 bytes of RAM, a one byte "framebuffer" where you draw through carefully timed updates of the register, and you only have the blanking intervals to do any real work. Developers had a choice of a 2kb or 4kb ROM cartridge, but only earned half the royalties if they used the larger and more expensive size. It encouraged efficient code.

I'm sorry for the very formal writing style, which made some people bristle at the first draft. I'm not really that way in person at all. It's just my background in things like Physics I guess. I tried make a less arrogant impression in the second draft but it's hard to change something that is so ingrained.

Again, thank you.


I am the K5 user now known as MichaelCrawford. I am not my corporation.


Old school, elitism (none / 0) (#84)
by Nagash on Fri May 10, 2002 at 08:11:34 PM EST

For someone who works in academia and teaches people from time to time, I must say that I do not fall into the "stereotypical academic" classification. I agree with you that being old school is generally a good thing. I wouldn't be pedantic about it, but yes, constant factor optimization is important. My recent programming effort will attest to that [1].

[1] I wrote a program in C (since I need the speed) to build minimal DFAs for finite languages. For debugging purposes, everything is a function call. When I profiled it, 83% of the time is spent in functions that could easily be macros and would not increase code size very much.

I think it's unfortunate that software gets pushed out the door too quickly most of the time. I, myself, like to be a bit more patient and make things right. I'm probably in the minority here, although I think the majority of people appreciate it when time is taken to make it right.

Woz



[ Parent ]
Missing the Forest (4.63 / 22) (#22)
by glyph on Fri May 10, 2002 at 04:33:22 AM EST

This is a typical C++ user article, with typical C++ user responses. As such, the author is sadly missing the forest, not quite for the trees, but for the rocks and the dirt and the grass and the bugs and the little green things which might be mold but aren't really mold because they're bioluminescent which is really interesting because normally you only find that underground and oh look there's a centipede with one hundred and one legs, I must take this home for some experiments!

Etcetera, etcetera.

This article does an excellent job of proving Space Monkey's point -- the lost productivity to C++ is lost to learning about exactly this sort of inane trivia that the C++ standards committee has made it their business to produce and then comment on for several years.

For example:

There are a number of different kinds of reference counting pointers. A popular one is the boost::shared_ptr provided by the Boost C++ library. Another is the thread-safe ZRef template written by Andy Green as part of the ZooLib cross-platform application framework. The most flexible are the family of policy-based smart pointers written by Andrei Alexandrescu in the Loki library. These are described in his book Modern C++ Design.

Why do I have to know this to make effective use of C++? Given that I could make effective use of C++ if I knew this (and a thousand other irrelevant details like it), how would this make me MORE productive than using a tool which did not require such a degree of arcane knowledge? And furthermore, how does a lack of garbage collection support in the language's core, confusing and contradictory data structure specifications, and surprising and unpredictable behavior of the standard library allow me to better "... plan the overall design of [my] product to use efficient algorithms, to minimize memory consumption, virtual memory paging, cache consumption and overall code size"?

I will not say that C++ is useless. Quite a bit of effort has been put into developing tools and libraries, so it can often be better to use C++ than a better tool without as much support. However, I will say quite emphatically that it is very unfortunate that our industry has wasted quite a bit of time writing tools and supporting such a beast.

The source of these problems is that there is no real model, formal or otherwise, underlying C++; you as a C++ user are harmed directly by this at the level of ABI incompatibilities between different vendors and the FBC problem, if you don't consider yourself "harmed" by the tremendous confusion between objects and values.

Stop the madness. Use a high level language. You'll be glad that you did.



good show (4.00 / 5) (#24)
by eLuddite on Fri May 10, 2002 at 05:56:07 AM EST

if you don't consider yourself "harmed" by the tremendous confusion between objects and values.

Bingo. Add to that the legacy of C's corruption of pass by value and pass by reference semantics (a pointer on the stack is not the same as pass by reference!) and the abstract notion of binding in general.

---
God hates human rights.
[ Parent ]

Value or Reference (none / 0) (#108)
by wnight on Sun May 12, 2002 at 01:48:38 PM EST

I'm not quite sure I follow you here. Can you elaborate?

How is passing a pointer not pass by reference? (Not that I've ever used the term, I've just always "passed a pointer" but I thought they were the same thing.)

How did C corrupt that (from what)?

And what abstract notion of binding?

---
Soldiers are ours, terrorists are theirs.

[ Parent ]

Pass by reference vs pass by value (none / 0) (#137)
by steveftoth on Wed May 15, 2002 at 03:15:52 PM EST

Pass by reference is declared in the function header like myFunction( & int i ); so that you call it, and the function recieves a pointer to the thing that you are passing it. That way if the function changes the value of the thing, it is prevelant when the function returns. Also, when you pass a pointer to a function that wants a pointer, the computer copies the value of the pointer, not of the object. You are passing the object by the VALUE of the reference. Not the object by reference. It's a slight distiction, similar to the one most people make when dealing with objects in java, because all objects can only be refered to via pointers ( references ) and when you pass them to a function in java, you copy the value of the reference and not the object. If you passed the object by value then you would have to create another object.

[ Parent ]
Binary compatible C++ (3.50 / 2) (#31)
by DodgyGeezer on Fri May 10, 2002 at 08:42:56 AM EST

"The source of these problems is that there is no real model, formal or otherwise, underlying C++; you as a C++ user are harmed directly by this at the level of ABI incompatibilities between different vendors and the FBC problem, if you don't consider yourself "harmed" by the tremendous confusion between objects and values. Stop the madness. Use a high level language. "

Or use COM with your C++. One of it's purposes is language neutral binary compatibility.

<ducks />



[ Parent ]
Language Mandates (5.00 / 1) (#38)
by warped1 on Fri May 10, 2002 at 10:41:54 AM EST

Stop the madness. Use a high level language. You'll be glad that you did.

So what high level languages would you recommend? Further, what language would you recommend for a computationally intensive program that had very demanding memory requirements? What about a serial driver for embedded work? What would you use for a program to handle large-scale network requests?

The point is this: Pick a programming language suitable to the problem at hand. One size does not fit all! Write your GUI in something other than assembly or C, and don't write scalable network services in visual basic.

[ Parent ]

No (3.00 / 1) (#45)
by Peaker on Fri May 10, 2002 at 10:56:09 AM EST

Don't use Visual Basic for anything :)

One size doesn't fit all, but that doesn't mean any given size must fit something, as Visual Basic is really useless, when compared with free and other costly alternatives (Python, Perl, Qt, Tk, Gtk+, etc).

[ Parent ]

Of course one size doesn't fit all -- however... (5.00 / 5) (#63)
by washort on Fri May 10, 2002 at 01:57:54 PM EST

Further, what language would you recommend for a computationally intensive program that had very demanding memory requirements?

A few things to keep in mind here: first of all, in most programs 10% of the code consumes 90% of the resources, and in many it's even a smaller fraction of code. When the performance of your high-level language is inadequate, it makes sense to write this portion in C. Second, I would argue that C++ isn't appropriate for many of those cases because it lacks garbage collection, which makes dealing with demanding memory requirements hard due to leaks. Also, oftentimes you can just get a bigger box! The cost of more powerful hardware is vastly outweighed by the costs of C++'s lack of maintainability and extensibility.

What about a serial driver for embedded work?

C, obviously. A serial driver is not a large program.

What would you use for a program to handle large-scale network requests?

Depends on the requirements; I've had remarkable success with Python lately. Python is extremely slow (~1000x slower than C), but has good integration with network services, so it's been suitable -- most network apps aren't CPU-bound. However, if I was to write something "large-scale", i'd probably use Common Lisp.

The point is this: Pick a programming language suitable to the problem at hand. One size does not fit all! Write your GUI in something other than assembly or C, and don't write scalable network services in visual basic.

Certainly! and most large projects need to be written in multiple languages; C for the parts that must be fast, high-level languages for the parts that dont. (Oh, and dont use visual basic for anything.)

[ Parent ]

Programming is a skilled job (none / 0) (#96)
by Anonymous Brave Guy on Sat May 11, 2002 at 02:18:05 PM EST

Why do I have to know this to make effective use of C++? Given that I could make effective use of C++ if I knew this (and a thousand other irrelevant details like it), how would this make me MORE productive than using a tool which did not require such a degree of arcane knowledge?

I think the problem with this argument is that, somewhere, you have to draw a line. Programming is a skilled job. It requires a level of knowledge and understanding, whatever tool you use. So where is the line? Can I expect programmers to know what a stream is? A thread? Not to compare floating point numbers for exact equality? Can I expect a C++ programmer to know how to use templates, a Java programmer to know good OO design principles, or an OCaml programmer to know the difference between declarative and imperative programming?

At the end of the day, some tools can be used easily, some tools can be powerful, but rarely can a powerful tool be used to its full potential easily. You can drive an automatic up and down the freeway all day, but every racing driver in the world has a manual gearbox. So it is with languages. Something like C++ can and should be used by very good programmers with demanding requirements. Mediocre programmers, or those whose requirements don't need the level of control that C++ makes available (at a cost in complexity) should use something else. Not everything is a nail, so why can't we have more than a hammer in the toolbox?



[ Parent ]
you argument is orthogonal to the issue (none / 0) (#98)
by eLuddite on Sat May 11, 2002 at 04:01:34 PM EST

Something like C++ can and should be used by very good programmers with demanding requirements.

The problem with your argument is that it blithely igores the fact that C++ is a terrible design. This is an important point: performant languages dont have to compile K & R C in order to be performant. You could design a low level language on top of an intelligent, language aware macro processing facility capable of expressing something like a template system, for example, that will knock the socks off C++ in every measurable and subjective way you care to contemplate.

The point is not that C++ compilers generate good or bad code, the point is that C++ is a terrible language.

Apropos your reply re: benchmarking, of course you are right; but your matrix library will still have to be built out of "synthetic" chunks of code that will exercise primitive operations such as indexing, call overhead and so on. If C++ function foo(a, b, c) dissassembles into more or less the the same sequence of machine instructions as the equivalent lisp function (foo a b c), then the lisp and C++ compilers generate identical code. And since I can write imperative, strictly typed lisp code as easily in lisp as I can write in C++, I can rewrite your matrix library in lisp without degrading its performance.

---
God hates human rights.
[ Parent ]

link addendum (none / 0) (#99)
by eLuddite on Sat May 11, 2002 at 04:35:38 PM EST

Look here to learn how Lisp includes type inferencing and assorted optimizations. If you are designing mathematical packages such as matrix receipes, CMUCL will, in point of fact, generate faster code than g++.

Granted, lisp programmers arent in the habit of littering their code with C like efficiency hints for the compiler, but they can if they want or need to, and when they do they're guilty of nothing worse than (((writing) elegant (C/C++)) code). At least they have that choice.

---
God hates human rights.
[ Parent ]

"under", not "on top of" (none / 0) (#102)
by eLuddite on Sat May 11, 2002 at 06:31:52 PM EST

You could design a low level language under an intelligent, language aware macro processing facility ...

Of course this is one of any number of options. It's not like it's hard to design something better than C++.

---
God hates human rights.
[ Parent ]

Not that terrible (none / 0) (#103)
by Anonymous Brave Guy on Sat May 11, 2002 at 06:56:01 PM EST

The problem with your argument is that it blithely igores the fact that C++ is a terrible design.

Not really. It's got a terrible syntax, certainly. While I appreciate the original need for that, for C compatibility years ago, I now view it as a liability.

Its underlying model, however, is a powerful and remarkably consistent one. The procedural, OO and generic models are all integrated quite cleanly, once you look beyond the clumsy syntax.

Obviously, C++ is not the only language in the world to have this power, and no doubt others have more. However, given its background, and consequent popularity, I think C++ has done pretty well overall.

Remember also that syntax is a personal taste thing. I am in favour of C++ style operator overloading for a variety of reasons, both cosmetic and technical, but although I'm somewhat familiar with the Lisp family of languages, I find most of their syntax to be awkward and unhelpful. YMMV, of course.

The point is not that C++ compilers generate good or bad code, the point is that C++ is a terrible language.

With respect, I think the point is that your arguments are based on emotion or theory, but there is little or no practical evidence to support them. C++ is a pragmatic language. It's not perfect, certainly; some of its flaws are horrible. But it does well enough for many people to solve many problems.

I've done a fair bit of research into other languages, similar or radically different, over the years. I've seen many examples of C++ used to solve problems large and small, in diverse problem domains. I have never seen a single successful, large-scale application written in Lisp, OCaml, Ruby, Python or any of the other "improvements", in any problem domain. Basically, none of the "superior" languages has anything like the scale of success of C++. If it's such a terrible language, and the alternatives are so much better, why is that?



[ Parent ]
ok (5.00 / 1) (#105)
by eLuddite on Sat May 11, 2002 at 07:57:31 PM EST

With respect, I think the point is that your arguments are based on emotion or theory

I think it's based on use. Whatever, it's not worth arguing about.

I have never seen a single successful, large-scale application written in Lisp Well, define large-scale. Certainly there are many Lisp systems larger than the average shrink-wrapped C++ app. Second, define "successful". Lots of prematurely shipped, buggy programs are "successful" because they are sold at a profit.

Here are some examples of large, successful lisp systems: Emacs, Gensym's G2 real-time expert system, AutoCAD, Yahoo Store, Igor Engraver (professional editing and publishing system for musical scores), Afferent (a system for molecular and cominatorial chemistry that helps pharmaceutical companies create new therapeutic drugs), ITA, and, of course, many artificial intelligence programs such as AARON.

C++'s market penetration has a lot to do with the contingent facts of the microcomputer and PC industry and history. Nevertheless, Lisp was -- and continues to be -- the superior choice for large and complex s/w systems. In fact, most of the things you take for granted today (computer graphics comes to mind) were originally implemented in Lisp.

---
God hates human rights.
[ Parent ]

Last post for now (none / 0) (#107)
by Anonymous Brave Guy on Sun May 12, 2002 at 06:52:14 AM EST

Well, define large-scale. Certainly there are many Lisp systems larger than the average shrink-wrapped C++ app.

OK, fair point. I was trying to avoid the old "million lines of code" benchmark, since that's inherently unfair when comparing languages.

I'll concede defeat on this point, since you're obviously better informed about Lisp than I am. I knew about Emacs, of course, but I certainly hadn't realised that AutoCAD was written in Lisp. Ah, well, you learn something new every day. Hey, I finally found a large app written in Lisp! :-)

I don't think that really detracts from my main point, however, which is that if C++ were as terrible as you claim, it wouldn't have had the success it has. It's not perfect, but it is pragmatic. It's stood the test of time well against "better" alternatives such as Java and C#, and it's thus far enjoyed a lot more success than languages like Ruby, Python, Lisp or OCaml, however much they may theoretically improve upon it. I think Bjarne called it right when he sacrificed a certain level of cleanliness in order to be compatible with what people already knew. In time, I expect the balance to shift (though unfortunately, truly good languages will probably never have the support they deserve, and I suspect well-marketed but inferior languages will remain dominant).

Well, that's about all I have to contribute here, I think. Thanks for an interesting and informative discussion.



[ Parent ]
C++ is way ugly... (1.60 / 5) (#28)
by buglord on Fri May 10, 2002 at 07:55:53 AM EST

...no matter what kind of style you use. I can't describe how I loathe the language.
It's been pieced together as an addon for an obsolete language. It's just like saying, "yeah, english is a really good language - you can use it all over the world, but nobody'll understand you if you don't talk like this and that..."
Languages should force developers to write efficient, readable code - everything else is just a poorly designed compromise.

I'm happy so much now I know how to use a gun!
Die Technik bereit und stabil... wir wollen zurück ins Telespiel!
welle:erdball - telespiel
Definition of C++ (1.00 / 3) (#29)
by eLuddite on Fri May 10, 2002 at 08:19:20 AM EST

C++ is the version of RATFOR that didnt have the good sense to die. Ouch, it hurts when I laugh.

---
God hates human rights.

Miscellaneous thoughts... (3.33 / 3) (#34)
by wiredog on Fri May 10, 2002 at 09:30:51 AM EST

Several comments here on the intricacy and size of C++. Looked at Java or Python (or Perl) lately? From what I've seen, in over 10 years programming, any general purpose language is going to develop huge numbers of monster libraries that make it impossible for any one person to master the entirety of the language. Heck, go to O'Reilly and look at all the Java and Perl titles. It will also develop various hacks that are designed to make certain things easier, while maintaining backwards combatibility, which cause headaches if used incorrectly. Java was originally marketed (as c# is today) as an easier to use, easier to understand, safer, and more portable C++. C++ was the same thing for C. Python is that, for Perl.

The most important thing a programmer can do is comment. Never assumne that you will know, a year from now when you look at the code again, what "float XMove;" is. Assume that someone else will be looking at the code and having to fix it.

The top of every header file should contain an ifdef to see if the file has already been included, to speed up builds.
//test.h
//this is a test header file
#ifndef TestH //have we already included this?
#define TestH //no, so set the define so we don't again
...various stuff
#endif //ifdef TestH


Peoples Front To Reunite Gondwanaland: "Stop the Laurasian Separatist Movement!"

Header protection (3.50 / 2) (#41)
by Peaker on Fri May 10, 2002 at 10:49:32 AM EST

Emacs code to ease header protection: (defun insert-header-protector (&optional header-name buffer)
(interactive)
(let ((header-name (or header-name
(let ((s (buffer-file-name)))
(string-match "\\(^\\|^\\(/[^/]*\\)*/\\)\\([^/]+\\)$" s)
(match-string 3 s))))
(buffer (or buffer
(current-buffer))))
(let ((header-text (replace-in-string header-name "\\.\\| " "_")))
(save-excursion
(set-buffer buffer)
(beginning-of-buffer)
(insert (format "#ifndef %s\n#define %s\n\n" header-text header-text))
(end-of-buffer)
(insert "\n#endif\n")))))

I would make the elisp more readable if I knew how to put spaces in K5 html.

It sure made me more easy about dividing into more header files :)

[ Parent ]

Non breaking space &nbsp; (3.50 / 2) (#43)
by wiredog on Fri May 10, 2002 at 10:53:14 AM EST

Puts spaces       in.

Peoples Front To Reunite Gondwanaland: "Stop the Laurasian Separatist Movement!"
[ Parent ]
For Emacs (none / 0) (#80)
by Hong Kong Phooey on Fri May 10, 2002 at 07:14:15 PM EST

http://emacs-template.sourceforge.net/

[ Parent ]
Wrong. (4.00 / 2) (#56)
by feldy on Fri May 10, 2002 at 11:53:03 AM EST

The top of every header file should contain an ifdef to see if the file has already been included, to speed up builds.

This is just plain wrong.  It should read: The top of every header file MUST contain an #ifndef (an internal include guard) to see if the file has already been included in order to prevent build errors.

The reason for having internal include guards is so that the content of a header file is not included multiple times in the same translation unit (a post-processed .C/.cc file).  If this happened, then you could get name collisions in the translation unit (e.g. a class defined twice) which would result in a compilation error.

Some people like to use external include guards to speed up build times.  External include guards are placed in the .C/.cc file around #include statements to prevent the compiler from even opening the header file if it's already been included in the current translation unit.  This can result in a speedup at compile time, especially if the source tree resides in a distributed file system, NFS/AFS, etc.  An example of an external include guard is:

/test.C
#ifndef testH
#define testH
#include <test.H>
#endif
/
...

feldy


[ Parent ]

Whoops (none / 0) (#59)
by wiredog on Fri May 10, 2002 at 12:06:07 PM EST

I meant ifndef. Had that in the code snippet even. Ah well.

Peoples Front To Reunite Gondwanaland: "Stop the Laurasian Separatist Movement!"
[ Parent ]
actually... (4.00 / 1) (#68)
by feldy on Fri May 10, 2002 at 03:04:10 PM EST

... I was giving you the benefit of the doubt with the ifdef/ifndef mixup; you did have it right in the code after all.  When I stated that the sentence was wrong, I was mainly referring to the reasoning -- that internal include guards are used to speedup compile time -- which isn't true.

To be honest, I think techniques such as internal include guards, which are necessary, and external include guards, which are just beneficial, demonstrate how poorly designed some aspects of c++ are -- although I guess these really should be attributed to c.  A preprocessor which does not understand the language at all and requires you too use such a kludge to preserve syntactical correctness is pretty awful.  Java's import feature is a much cleaner design.

feldy

[ Parent ]

Just don't overdo it (4.00 / 1) (#81)
by Hong Kong Phooey on Fri May 10, 2002 at 07:31:13 PM EST

Unless it is a program written with the intent to educate newbie programmers, comments like


#ifndef TestH //have we already included this?
#define TestH //no, so set the define so we don't again

are pretty pointless.  Too much obvious comments can make the code harder to read.

[ Parent ]

I agree (4.00 / 1) (#92)
by dublet on Sat May 11, 2002 at 08:00:15 AM EST

As I read somewhere (could have been a book, or docs) comments should be used to elaborate on code, and you shouldn't rewrite all the code in 'human language'
For example:
for( int i = 0; i <5; i++ ) // make five iterations <BR> or
if( result = -1 ) // if result of the program is an error
would be utterly useless, isn't that what the code says? if you comment like that it just clutters the code.

Badger. Badger. ←
[ Parent ]
if (result = -1) (4.00 / 1) (#97)
by PurpleBob on Sat May 11, 2002 at 03:14:50 PM EST

That comment would be downright misleading next to a mistake like that (= instead of ==).

[ Parent ]
whoops.. (none / 0) (#143)
by dublet on Fri May 24, 2002 at 09:35:24 AM EST

sorry, heh.. sorry for forgetting that little '=', doesn't usually happen to me :)

Badger. Badger. ←
[ Parent ]
Nice Article (4.40 / 5) (#37)
by Simon Kinahan on Fri May 10, 2002 at 10:18:19 AM EST

A big improvement on the original, thanks. All the advice you give is good. I'd also recomment anyone interested in these issues read "Effective C++" and "More Effective C++". Actually, I'd recommend any C++ programmer be required to read them before being allowed near a keyboard.

Two things you forgot that I'd point out: You shouldn't store objects by value if its possible their runtime type may vary (ie. if you have any interest in polymorphism). You shouldn't have any "normal" pointers that need to clean-up their referrents between the throw and catch of an exception, which comes down to "always use auto_ptr", or near as dammit.  

Having said that, I don't use C++ for anything unless its unavoidable (ie. a client requirement, or a huge payoff due to the availability of libraries etc), and its exactly the kinds of issues you discuss that are the problem. If I need ultimate speed, I use C. If I don't I use a higher level language.

So whats wrong with C++ ? All the rules that have to be remembered in order to use it. The original "C with classes" has become shrouded in so many layers of cruft, you now have to remember which features don't work correctly together, and, generally, limit yourself to a subset of the language that has some kind of sensible semantics. Its not a question of what you need to be efficient: its what you need to do to keep the language out of your way. It also has the problems with team-work that any "more than one way to do it" language will have: you'd better make sure you're all doing it the same way, especially given the hazards style conflicts involve in C++.

Given these difficulties, I don't understand why people use C++. If you want to control every detail of you program's execution, use C or assembler. If you want to concentrate on solving the problem and hand, and are less worried about streamlined performance, use a higher level language. Java is the most popular option, but its not the only choice.

As for being "old school": The desire for performance, just like the desire for any other property of a system, has to be traded off against the time it takes to achieve and the willingness of your client to pay for it. If you spend weeks getting every last gram of performance from a program that will run as a network server, you're ripping off whoever is paying. This is why people use Java or Smalltalk or SML or foo instead of C++ or C: if you don't have tight performance constraints, using a language which lets you concentrate on the solution to the problem at hand and forget the underlying machine saves time, and such languages are becoming more and more usable as runtimes become more able to optimise for the underlying architecture.  

Having said that, you're quite right that the kind of performance that is worth having has to be designed for. At the very least, proper modularisation allows you to swap out poorly performing components and replace them. Better still, there are particular optimisations (such as object pooling in Java) that it helps to plan for. Best of all is proper architecture: any design that requires a single thread of control to see everything won't scale (for instance).


Simon

If you disagree, post, don't moderate

minor nit (5.00 / 2) (#71)
by eLuddite on Fri May 10, 2002 at 03:57:57 PM EST

if you don't have tight performance constraints, using a language which lets you concentrate on the solution to the problem at hand and forget the underlying machine saves time

Compiled high-level languages are just as fast as C++. Yes they are.

---
God hates human rights.
[ Parent ]

Its often very close, yes (4.50 / 2) (#73)
by Simon Kinahan on Fri May 10, 2002 at 04:07:29 PM EST

However, the issue when people are talking about performance problems in high level languages is often not so much speed as such as predictability. The best technology for accelerating Java, Smalltalk or Lisp tends to be adaptive, that is, it responds to what the program is going. That adds a new level of complexity to predicting performance, where as C allows you to control precisely what happens when, and C++ tries to do the same.

For instance, dynamic, partially evaluating compilers (like HotSpot), compile only the most common cases. Generational garbage collectors use different collectors and different spaces depending on the profile of objects being created and dieing. While there are more deterministic algorithms than can be substituted for these, they tend to be slower overall.  

Simon

If you disagree, post, don't moderate
[ Parent ]

Major nit (5.00 / 1) (#94)
by Anonymous Brave Guy on Sat May 11, 2002 at 02:04:44 PM EST

The problem with surveys like those is that while they are broad, they are not deep, or vice versa. Comparing programming languages objectively in full depth requires a level of specialist knowledge across such a wide spectrum of fields that almost no-one could do it, and to my knowledge certainly no-one has.

To give a prime example, consider the first survey you mentioned. If we compare the matrix multiplication example for C, C++, Java and OCaml, we find that the C, C++ and Java and OCaml code is almost semantically identical, though it varies slightly in linguistic syntax. They are all variations on the C theme; the C++ source is nearly identical in syntax as well.

Further, in spite of the way real world code in these languages would probably be written, the C++ example doesn't use a matrix type, it's all procedural. The Java example's matrix type is just a language-forced wrapper for essentially procedural code (note that everything in it is static). The OCaml example isn't quite comparable here, given the presence of Array.make_matrix, but it still very much uses the imperative features of OCaml to write much the same algorithm in much the same way as used in the other examples here.

Basically, all four examples should produce almost identical benchmarks, because they should compile down to almost identical code. The memory use is, unsurprisingly, almost identical as well, with the only exception being Java. That is also a rather unfair reflection, though, since the overheads imposed by Java are normally quite small, but appear massive here since the compiled code for such a simple example will itself be tiny.

Essentially, the test is comparing artificial examples of the languages that look little or nothing like real world code would, and then drawing conclusions about how the languages would work in reality based on that flawed foundation. If all of your artificial examples are written in essentially the same way, it's not surprising that the results are similar.

If you really wanted to test the power of these languages in using matrices, you'd be comparing something like a low-level library of matrix-manipulation functions in C (a procedural approach) with a good matrix library in C++ (try Blitz++, to allow for the benefits of using expression templates and such that can't be gained from some other languages), a good purely OO matrix implementation in Java, and a decent matrix module in OCaml.

You ought to be running a range of calculations on these things, including simple things like transposition and equality testing, more involved operations like multiply-and-add and full-scale matrix maths like the calculation of a determinant and subsequent inversion. All of these should be used in the context of some realistic calculations, e.g., a test routine that performs the transformations typical of 2D graphics work (but without actually outputting any graphics, obviously).

Now, compare the scale of this reasonably representative test with the small-scale, unrealistic code samples used on the site in question, and observe how the conclusions drawn there cannot possibly be justified purely on the basis of the evidence they provide.

BTW, I pick this particular example because I'm very familiar with it due to my particular work experience in the past. I know for a fact that things like Blitz++ knock the socks of most languages in practice, that C can come close but is much more prone to flaws if you're careless about optimising multiply-and-add ops and such, and so on. It really is the case here that while all languages are equal, some are more equal than others.

I think that's made the point about how, while they are interesting, such comparisons as you cite should not be relied upon in isolation. I could similarly dismantle the conclusions of the other survey, but I'll settle for pointing out that the survey of Lisp programmers seems to have been carried out some time after the survey of C++ ones, and by their own admission, the use of a self-selecting sample of Lisp programmers is a major deficiency in the method. Again, it's an interesting result, but hardly firm scientific data.



[ Parent ]
that's all well and good; unfortunately... (5.00 / 1) (#101)
by eLuddite on Sat May 11, 2002 at 06:14:29 PM EST

it's very hard to reply to idly abstract and anecdotal speculation. OTOH I only have to invoke CMU-CL to demonstrate that, although you made well intentioned and reasonable sounding noise, it's still prima facie incorrect noise. A fact is a stubborn thing; the fact is, I can write lisp at the low level equivalence of C++. And because lisp macros are a full-fledged code and syntax generation system, I can do so with at least as much genericity as a C++ templates.

I'm afraid I'm going to have to insist upon this point: you have to work immoderately and intimately with your compiler and specific platform in order to write C++ code that will run even a little faster than code generated by any number of excellent HLL compilers.

This position is not negotiable -- why wouldnt it, couldnt it be true?? For example, a language such as Common Lisp has a standard way of "dropping into" the kind of compiler hinting syntax and semantics (eg, "this variable is strictly integer") that's definitive of and unavoidable under C/C++. There's nothing special about the C++ language which makes C++ compilers necessarily faster than compilers for other HLLs.

---
God hates human rights.
[ Parent ]

Valid arguments here (4.50 / 2) (#40)
by caffeine1 on Fri May 10, 2002 at 10:44:05 AM EST

Until very recently, I was using C & C++ in the embedded space, where it's absolutely critical to know what the compiler, linker, and assembler are actually doing. And, sadly, I saw a few programmers here and there who didn't have that attention to detail when using C++.. even in interrupt threads!

A few tips I would add, especially for the embedded crowd:

  • Being old school not only provides you with extra cred; it's absolutely essential.
  • If you can, use an embedded platform that will allow you design on a non-embedded system first, like Embedded Linux. At some point, you must develop on the target platform, but you can't use gprof when your code has been loaded onto the processor of your board.
  • Know the language intimately. If you don't, buddy up with someone who has the experience, and learn from them, even if it means fetching his/her coffee. Learn from their experience; you'll be glad you did.

Having said that, I'll also say that there's no substitute for attention to style and detail. Intricate knowledge of protocols or software packages is nice, but at the end of the day, you're judged by the quality of your code.

Coding vs Maintenance (4.16 / 12) (#42)
by gidds on Fri May 10, 2002 at 10:50:40 AM EST

It's interesting to see all these comments on the advantages or disadvantages of languages.  Not because I don't agree with some of them, but because of the criteria people are using: in the main, people seem to be considering only the writing of code, and not its maintenance.

If I give you a complex problem, and ask you to code a solution from scratch, then you'll probably have your own favourite language in which to do so.  If I give you a large, complex piece of code that someone else has written, and ask you to change it, then you'll also probably have a preferred language you'd like it to be in.  But in many cases I bet it's not the same language!

In many ways the two situations have opposite requirements.  The more powerful a language is to code in, the more complex will be other people's code.  The more choices a language gives you, the less likely that someone else will have chosen the same.  The more subtlety there is in a language, the easier it is to miss things in other people's code. The more scope for `clever' shortcuts, the longer you'll need to spend understanding other people's shortcuts.  In short, complex kitchen-sink languages like C++ make maintenance a nightmare.

Now, given that in general (especially in the corporate world) far more time is spent maintaining software than writing it, it rather worries me that people don't seem to be consider maintenance important.

Okay, my own perspective: I know C inside out, have read a lot about C++ but coded very little, and am a convinced Javaphile.  I know that many of you have instantly dismissed me as a loser, but stay with me.  I you were given ten thousand lines of badly-written C++, or ten thousand lines of badly-written Java, which would you find it easier to get to grips with?  "Ah, but I only write good code!" I hear you cry.  Well, maybe; no-one thinks they write bad code, but an awful lot still gets written.  And people still have to maintain it.

Of course, that's not the only reason I prefer Java; I think it's an ace language all round.  I find it very powerful and expressive without the arcane complexity of certain other languages.  (And as a side note: many objections to it, such as `garbage collection is inherently slow', `bytecode can never be as fast as compiled code', and `multiple inheritance is vital', are a little out of date.)  Above all, it's neat, and that neatness encourages good code.  It's not perfect, of course, but the compromises it makes are better than in any other language I've seen.

But this isn't meant as a Java advocacy screed.  It's simply a request that people think of maintenance when they code; some languages make this easier than others.  If everyone wrote code in the knowledge that someone else would be maintaining it, I think that the quality of code would rise considerably.

Andy/

Maintainance (3.50 / 2) (#55)
by Cro Magnon on Fri May 10, 2002 at 11:32:16 AM EST

I couldn't agree more! Considering how much bad code I encountered in COBOL, I hope I never have to maintain code written in an "unreadable" language. I might write good code, but way too many other programmers don't, and they're the ones I have to clean up after. People claim you can use a reasonable subset of C++, but what other programmers call reasonable, I might call "tricky code", or vice-versa.
Information wants to be beer.
[ Parent ]
I write crappy code! (5.00 / 5) (#62)
by gte910h on Fri May 10, 2002 at 01:43:57 PM EST

"Ah, but I only write good code!" I hear you cry. Well, maybe; no-one thinks they write bad code, but an awful lot still gets written. And people still have to maintain it.

I write crappy code that "works right now". Then I go back and turn it into "right code" that works well. This two pass approach yeilds programs really quickly when I have a design.

However when a boss, co-worker, crisis, orangutang, etc, cuts me off from making that second pass, the stuff I write is HORRIBLE, completely unreadable, with really cryptic comments. I have run into that problem a couple times with bosses trying to schedule other stuff over that clean-up time. When I was having a code review over all my code (some first pass and some second pass code), the reviewer asked 1) Had I wrote all the code and 2) Have I ever beed diagnosed with split personality disorder. After I explained the process, he recommended that I don't place my name in the author field of the comments until AFTER I get that second pass in.


--Michael

[ Parent ]
Always write code twice. (none / 0) (#138)
by steveftoth on Wed May 15, 2002 at 03:50:57 PM EST

How many books are released into the wild with only one or 2 drafts? Almost none, most books have multiple drafts with many problems and countless errors. There needs to be a larger movement to increase the number of code 'editors' to make sure that code is not released into the repositories of many a company without being at least overviewed by another person. I totatly agree with you, that most of the time, the first line of code that you get to work are very much ulgy and disgusting. For me this is because I try to get it to work before concentrating on what the variable names look like. After I settle down and get it working, then I go back and clean it up. This seems to be a problem in many shops. The programmer is forced to move on rather then clean old code. Many times it is better to write the whole thing again after the problem has been solved. One reason why smalle software is better, as the programmer can actually have the entire solution in their head while writing it.

[ Parent ]
Somebody had to say this.. (4.55 / 9) (#50)
by caffeine1 on Fri May 10, 2002 at 11:09:31 AM EST

Objective-C is simpler and easier to use, hands down.

Don't get me wrong; I'm not anti-C++, having used it for about eleven years. But, for giggles, now that my employer has provided me with some free time, I'm learning how to program for MacOS X and Cocoa.

(Annoying aside: Why? Because I want to. No other reason. I'll worry about honing other skills once this is off my to-do list. Know anyone who wants a Cocoa programmer by the way?)

My early impressions are that, yeah, ObjC is an intrusive OO language (that is, it provide a root "Object" class like Java, forcing some of its taxonomy on the developer), which almost guarantees the need for a run-time mechanism for reflection, etc. But, unlike C++, the syntax is a lot simpler, a lot cleaner.. a lot more elegant.

Not that I'm faulting C++ here. One of the requirements of the language was 100% backward compatibility with legacy C, in semantics as well as syntax. (Stroustrup originally called C++ "C with classes".) That means no intrusiveness, and no run-time funny stuff except for the absolutely necessary, such as virtual function tables and name mangling at compile time for resolving instances of polymorphism (instead of run-time table lookups). So you have an object-based language at least that can be used anywhere C can, even at the driver/kernel level.

At the same time, however, I'm finding ObjC to be a much simpler syntax, with simple semantics to match. All method calls reduce to message passing. There's rudimentary reflection built in, as well as support for dynamic loading of modules. Not a bad language for apps at all. It's a pity it never really caught on, actually.

He's not kidding (4.00 / 4) (#86)
by Alec Onasis on Fri May 10, 2002 at 08:44:30 PM EST

Objective C realy is that much better then C++...I used to love C++ and I still like it in some instances but Objective C is so much better.  You didn;t even mention things like @selector which for those that don't know lets you call a method by a string name!!!  This is programming people!  I can, if I wanted to, have my program enter a function by typing the name of it in a text field or something.  

Not to mention the emory allocation methods are better (reference count). It takes time tor eally learn the Foundation (Which is available for many platforms) and then with the apple exclusive App Kit (the part that manages a UI) it is just a pleasure to work with.

I don't know, I really think it is time people seriously look at this...they won't as the MFC is written in C++ but oh well, there are still a lot of operation using objective C.

[ Parent ]

No, really (3.33 / 9) (#66)
by trhurler on Fri May 10, 2002 at 02:33:40 PM EST

C++ is an abomination to society, and is doubtlessly responsible for hundreds of millions of lost hours of productivity. You mention many of the reasons in your article. The whole delete vs delete[] thing is a sick joke, and frankly you CANNOT write C++ that will end up as "good code" after compilation unless you only want it to work on one compiler. The language has lots of complexity and traps for the user that are completely pointless, and even if you can find a set of rules for programming that avoid them, that doesn't make it any better; you should not have to do this just to write code.

In summary, C++ sucks the greasy cock of Satan. Thank you for your time.

--
'God dammit, your posts make me hard.' --LilDebbie

hours of lost productivity (none / 0) (#100)
by ucblockhead on Sat May 11, 2002 at 05:17:32 PM EST

If C++ was causing thousands of hours of lost productivity, you'd think that the free market would force software companies to use a different language, wouldn't you?
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
Yes, actually (none / 0) (#110)
by nevauene on Sun May 12, 2002 at 04:42:24 PM EST

you'd think that the free market would force software companies to use a different language, wouldn't you?

They do, and it's called Java. <shudder>


There is no K5 Cabal.
[ Parent ]
The Steel Handkerchief of PolitenessMan (none / 0) (#117)
by GoingWare on Mon May 13, 2002 at 05:26:38 AM EST

Might I suggest that instead of hurling obscenities, you write an article of your own that discusses the finer points of your favorite programming language?

If you wrote about, say, Java, I might politely mention on or two factors that I consider a disadvantage. But even though I'm no fan of Java, there are some things I like about it and I would acknowledge them.

I wouldn't show up in discussion of some other programming language and say such rude things. I think that's very inappropriate.


I am the K5 user now known as MichaelCrawford. I am not my corporation.


[ Parent ]

Be sure to wipe your chin (1.00 / 1) (#121)
by trhurler on Mon May 13, 2002 at 11:18:43 AM EST

If I cared what was inappropriate, I wouldn't tell you to toss an incontinent geezer's salad, now would I? Probably not.

--
'God dammit, your posts make me hard.' --LilDebbie

[ Parent ]
Dylan solves most OOP issues (3.00 / 3) (#67)
by jared on Fri May 10, 2002 at 02:59:11 PM EST


Quick summary of nice Dylan features.

Java vs. Dylan.

C++, C, Objective C etc. vs. Dylan.

Dylan is fast and neat.

Intro to Dylan features.

Thank you for your time.

Well (2.75 / 4) (#74)
by Hong Kong Phooey on Fri May 10, 2002 at 04:59:41 PM EST

You left out the part explaining that Dylan is pretty much a dead language.

[ Parent ]
Goo (4.66 / 3) (#79)
by kimbly on Fri May 10, 2002 at 06:40:12 PM EST

Goo is a new language by John Bachrach, one of the people who worked on Dylan at Apple. Goo's design is strongly based on dylan's. You can find Goo at www.googoogaga.org.

Goo was recently released under the GPL.

I was first introduced to Goo at the Lightweight Languages conference at MIT's AI lab. The tradeoffs made in its design really got me excited. For example, it emphasizes quick compile-edit-test cycles, and a very small compiler codebase. But, in tension to these principles, it also uses global optimization, with a dependency-based incremental compiler in order to keep the turnaround time low. I came away from the presentation remembering the thrill of when I wrote graphics programs on a 286, getting major performance gains out of attention to little details like cache misses and integer arithmetic.

[ Parent ]

Attention to the cache (none / 0) (#116)
by GoingWare on Mon May 13, 2002 at 05:23:00 AM EST

Paying attention to the cache makes even more of a difference on modern processors than old ones, because the CPU core runs so much faster than main memory.

On the PowerPC 603e, I was able to double the speed of a common code path in the Mac OS by paying careful attention to cache usage.

Tips for cache speedup:

If you use any memory at all, use it as much as you can until you're done, use the memory right next to it to, but when you're done, don't use it again.

Order the member variables in your classes and structs so that those that are used together in time are sitting next to each other in the header file.

Don't have classes with lots of members that are unrelated in their purpose. If you have a bunch of members that don't really go with the others, put them in a separate class.

You may think you have 128 MB of RAM to run on a consumer PC, but you don't. You have 256 kb of cache. If you remember that constraint your programs will run fast.

Even if you think you have 128 MB, you really don't. You share the ram with the OS and other applications. I would suggest that 16 MB of RAM might be an appropriate target for today's applications to try to fit into; any more and you'll start paging (because you're sharing space with other applications), which slows you down tremendously. And I'd only take that much if my application was tremendously important; small utilities should be much smaller.


I am the K5 user now known as MichaelCrawford. I am not my corporation.


[ Parent ]

Nice comparisons (4.25 / 4) (#89)
by ttfkam on Fri May 10, 2002 at 09:44:28 PM EST

Let's see...  You've got a Dylan vs. C++ review that was written in 1995: seven years ago and three years before the standard came out.

#1 Errata:
 Compares a Dylan if-statement with a C++ initializer.  It skips over that the C++ if statement is just as readable as the Dylan example.

#2 Errata:
 Skips over the fact that reference counters (smart pointers) and garbage collectors have been available for C++ for several years.  Maybe it's the document's age showing again.

#3 Errata:
 Constructors, desctructors, and type conversions lead to excessive speed problems in C++?  Yup.  The document's seven years old.  As for manually deleting objects as they die, see previous note regarding C++ garbage collectors.

#4 Errata:
 "C++'s lack of any standard libraries..."  Says it all right there.

#5 Errata:
 Misses the whole fact that builds no longer take two days to complete.  If your C++ code is sufficiently modular by design (you did have a design guideline right?) and you follow the guidelines listed in the excellent article above, this is not a real issue anymore.  We aren't working on 386's with 4MB RAM anymore.

#6 Errata:
 "Most C++ debuggers are unable to determine exactly what kind of object a pointer references."  Hmmm, I must have lucked out on the debuggers I have used because I haven't found it to be problem.  ...or maybe it's the fact that C++ has runtime type information now.  ;-)

#7 Errata:
 Oh my god!  They didn't even try to avoid temporaries!  Coupled with a complete glossing over of C++ templates (probably due to the document's age) and you have crap code trying to prove that C++ is inferior.

#8 Errata:
 Unfortunately Dylan does not have the expressiveness possible with C++ policies (templates + MI).  See the STL for examples.

#9 Errata:
 Nothing incorrect -- merely that C++ is always staticly compiled.  Can be both a blessing and a bane.

#10 Errata:
 ISO-98 C++.  Seems like a standard to me.  Oh yeah, old document.

And let's not forget an important addendum: in 2002 people actually use C++ en masse.  As an employer, try finding a good Dylan developer as easily as finding a good C++ developer.  For better or for worse, C++ does the job well enough for most people to use it instead of Dylan.

------------

As a final stab, while Dylan may slam on Java because of Java's lack of MI and dispatch methods, does Dylan have an equivalent of EJBs and the totality of the Java standard libraries?

Also, taking the IDLExpression "evaluate()" method, the author assumes that since this is the proper way of solving the problem in Dylan that the Java example is automatically deficient.  Kind of like someone transcribing Spanish to English one word at a time to show how bad English is as a language.  We're talking apples and oranges here; there is no direct translation.  Add in the future support for templates in Java (slated for 1.5 I think) and Dylan's advantages even in this contrived example melt away.

You like to code in Dylan?  Good for you.  Does it work for you?  Good for you.  Different strokes for different folks.  There is no "one" language.

If I'm made in God's image then God needs to lay off the corn chips and onion dip. Get some exercise, God! - Tatarigami
[ Parent ]

storing member variables by allocated pointers???? (4.75 / 4) (#82)
by AntiPattern on Fri May 10, 2002 at 07:47:59 PM EST

I disagree with this recommendation. If it makes more sense to store a member by value, go ahead and do so. Don't add complexity to the code just to avoid including a header file. By needlessly using a pointer, you'll have to make sure that it's handled properly in any copy constructor, assignment operator, or destructor.

pay attention to what you're saying!! (2.50 / 4) (#85)
by washort on Fri May 10, 2002 at 08:30:40 PM EST

This is the 80-20 rule; 80 percent of the runtime is spent in 20 percent of the code - but you're likely to find that most of the complexity is in the 80 percent of the code that doesn't affect performance significantly, so you might as well make it maintainable rather than trying to make it fast.

Precisely why C++ is usually a bad choice. And often it's more like 90/10. Choose a language with garbage collection and pointer safety. If that last 10% is too slow, write that portion in C.

Dont perpetuate the maintenance and extensibility nightmare that is C++.

Garbadge collection cause it's own problems (none / 0) (#95)
by mtilsted on Sat May 11, 2002 at 02:10:22 PM EST

But garbadge collection cause it's own problems, because you lack a destructer. This is a problem with files because you don't know when the file is being flushed/closed, unlike in c++ where your file would be closed as soon as the file object ran out of scope. Ofcause you can just call close/dispose/whatever on objects you need to destroy/close but then you have all the problems with not having garbadge collection.

An example where this is a big problem is with Eclipse(A software development tool/editer) where you have to destroy all objects dealing with resources manully by caling the method dispose().
when you don't use them anymore.

What I really miss is a language where you can have both garbadge and non-garbadge collected objects.

Martin Tilsted
 

[ Parent ]

Implementation dependent (none / 0) (#119)
by x31eq on Mon May 13, 2002 at 07:35:18 AM EST

You seem to be assuming something like a mark and sweep algorithm. With a dumb reference counting system you can ensure objects will be destroyed as soon as they go out of scope. No need to explicitly delete objects you have a pointer to, but you do need to explicitly break cyclical references.

The current CPython implementation uses immediate reference counting collection backed up by a cyclical collector. That means you can usually predict when a destructor will get called, but don't have to worry about memory leaks. Unfortunately, the language definition can't mandate this behaviour because it has to be consistent with the Java Virtual Machine.

If you don't mind tying yourself to one implementation, you could probably use this two-stage process with any language that supports automatic memory management.

Your mixture of objects that do or don't need explicit collection sounds problematic to me. It means an extra level of complexity the programmer has to deal with. Why not explicitly close a resource instead of explicitly taking control of an object's memory management? It also gets messy if an object being implicitly managed holds a reference to an object that isn't. Nothing you can't deal with, but lots of problems I'd rather not have to deal with. Which is the reason for using garbage collection in the first place.



[ Parent ]
Reason and fact, not emotion, please (none / 0) (#104)
by Anonymous Brave Guy on Sat May 11, 2002 at 07:07:46 PM EST

Choose a language with garbage collection and pointer safety.

C++ can have both of those things. Any well-written C++ will routinely make use of smart pointers and container classes instead of raw [] and * constructs, at the least. This is one of the first and foremost advantages it holds over C.

Further, since C++ doesn't allocate everything dynamically as many comparable languages do, GC is much less of an issue. The advantages conveyed by deterministic destruction -- still not matched by "superior" languages like Java and C# -- far outweigh the effort required to set this up (which is invariably pretty minimal in real code).

Dont perpetuate the maintenance and extensibility nightmare that is C++.

If C++ was such a maintenance and extensibility nightmare, it wouldn't still be here a couple of decades later, and it wouldn't still be one of the most popular languages in the programming world (still second only to C, and sometimes not even that, in any benchmark I've seen in the past year).



[ Parent ]
Refactoring (none / 0) (#106)
by smallstepforman on Sat May 11, 2002 at 08:28:16 PM EST

Nice post Michael, I'm sure a lot of people learning programming will appretiate some of your points.  I also used to thrive reading such tutorials (how to be a better programmer), that is, I was looking for the <B>Holy Grail</B>, so to speak.  And guess what Michael, I ended up finding it.

For everyone desperately seeking the <B>Holy Grail</B> of software development, I would urge you to check out the book from Martin Fowler "<I>Refactoring</I>".  I have never read an article/publication which has shown me the programmers light, figuratively speaking.  It REALLY is that good.

Read it if you haven't, you can thank me later.

Refactoring in Wiki & my example (none / 0) (#115)
by GoingWare on Mon May 13, 2002 at 05:12:14 AM EST

The WikiWikiWeb has a a page called RefactorMercilessly in the discussion of Extreme Programming. That's where I first learned about refactoring.

I used to work for a guy that dispensed this bit of wisdom upon me: "Never touch working code". He was very insistent on this, and demanded that once we got something working in a program we never revisit it. He was also fond of "Copy 'n Paste reuse". It took a long time to unlearn this.

I have an example of refactoring in one of my own projects where I discuss the approaches I took to a memory management problem starting with a straightforward approach that leaked like a sieve, to an eventual solution that was leak free and exception-safe.


I am the K5 user now known as MichaelCrawford. I am not my corporation.


[ Parent ]

private: broken in C++ (3.66 / 3) (#113)
by cathryn on Mon May 13, 2002 at 04:11:24 AM EST

The main problem with C++ is that the private: thingy is broken. C++ requires you to place private local variables in the class up in the .h file. So, what happens is even if you are super careful about only including the right include files. Sometimes when you want to just change the implementation of a class by adding a new private variable you force a re-compile on every file that accesses that class. This is stupid! If the language is modular, why should changing the implementation of a 'module' force a recompile of everything that accesses it -- even if I'm only changing Private stuff. Arrrgh. The long compile times are catastrophic for keeping big C++ projects in order. What tends to happen, then, is that programmers, faced with the choice of going home and having a life or sitting around watching half the project recompile, hack in crappy code just to get it done quickly. Even if touching the class is the 'right thing' nobody does it because that's painful from the recompile perspective. This is an important poitnt. If compile times suck, then the structure of the code is going to disintegrate. And C++ used naively results in hellish compile times. Putting Privates in the headers means the other objects are all parsing all this useless junk, and that slows down the compile even more. The only way to get around all this hell, is to resort to hacky crap, like keeping private variables in pointers to classes in your code (pimpl) -- or doing inheritance hacks to keep the private variables only in the local CPP file. This works, and keeps down compile times -- but usually introduces useless extra instructions. These hacks further obfuscate the code and maintain the 'C++ mega-guru' write-only code phenomenon. Bleach.

private broken in C++ (3.00 / 2) (#114)
by cathryn on Mon May 13, 2002 at 04:13:30 AM EST

The main problem with C++ is that the private: thingy is broken. C++ requires you to place private local variables in the class up in the .h file. So, what happens is even if you are super careful about only including the right include files. Sometimes when you want to just change the implementation of a class by adding a new private variable you force a re-compile on every file that accesses that class.

This is stupid! If the language is modular, why should changing the implementation of a 'module' force a recompile of everything that accesses it -- even if I'm only changing Private stuff. Arrrgh.

The long compile times are catastrophic for keeping big C++ projects in order. What tends to happen, then, is that programmers, faced with the choice of going home and having a life or sitting around watching half the project recompile, hack in crappy code just to get it done quickly. Even if touching the class is the 'right thing' nobody does it because that's painful from the recompile perspective.

This is an important poitnt. If compile times suck, then the structure of the code is going to disintegrate. And C++ used naively results in hellish compile times. Putting Privates in the headers means the other objects are all parsing all this useless junk, and that slows down the compile even more.

The only way to get around all this hell, is to resort to hacky crap, like keeping private variables in pointers to classes in your code (pimpl) -- or doing inheritance hacks to keep the private variables only in the local CPP file. This works, and keeps down compile times -- but usually introduces useless extra instructions. These hacks further obfuscate the code and maintain the 'C++ mega-guru' write-only code phenomenon. Bleach.

Sorry for dupe. posted without checking

It's from C heritage - needed for sizeof() (4.00 / 1) (#120)
by GoingWare on Mon May 13, 2002 at 07:55:30 AM EST

This has always bothered me too. But it's really unavoidable, you need to have all of the member variables (including the private ones) declared in order for the sizeof() operator to work, so new can know how many bytes to allocate for a class instance.

Also, Bjarne Stroustrup explains somewhere (I think in the Design and Evolution of C++) that he wanted private variables to be visible to the compiler. One reason is for debugging; if you comment off the private: keyword, you can make all your members public but you won't break any code.

Structuring your header files using forward references can help minimize this problem, but it's still an issue if your headers reference templates or include inline functions, which usually need the full declaration rather than a forward reference.

If you find this is really a problem for you, I strongly recommend you read John Lakos' Large Scale C++ Software Design. There's a lot you can do to minimize this problem, and many of the things he suggests have the effect of making your code much more understandable and maintainable, and not just faster to compile.

I discuss this in some more detail in the original article Pointers, References and Values at Avoiding Unnecessary Header File Inclusion.


I am the K5 user now known as MichaelCrawford. I am not my corporation.


[ Parent ]

Stroustrup addresses this (none / 0) (#122)
by avdi on Mon May 13, 2002 at 11:57:03 AM EST

Basicly, it never occurred to him that just because you don't have an explicit 'interface' keyword in Java, that people would try to cram everything about a class, interface and implementation, into the class definition.  Just because there's no 'interface' keyword doesn't mean that good C++ code doesn't use interfaces.  Well-encapsulated C++ code puts the public interface to a class in an abstract interface class, typically called something like 'MyClassIface' or 'MyClassIf'.  Then the implementation is derived from this interface.  Most client code can use just #include the interface definition, and use the interface type for manipulating objects.  Only the code that actually constructs the object needs to #include the implementation definition header.

--
Now leave us, and take your fish with you. - Faramir
[ Parent ]
Yeah, (none / 0) (#125)
by cathryn on Mon May 13, 2002 at 04:36:41 PM EST

And, that all works, but it's just extra work.  The problem is that just sticking all the locals in the private: for the class is the straightforward way to use the language.  And, at the beginning of the project putting privates in the class is no problem.  It looks neat, it works, it's well-structured.  Fine business.  So, programmers write nice neat code this way, and then when the project slowly scales up it all melts down into a giant mess that takes forever to compile.

Usually, for me, I'm never there when these things start.  So, I'm just speculating on the genesis of all this.  I always come in after a few years have gone by, and the project takes 2 hours to build, and management is wondering why everyone's sitting around browsing the web and not getting any work done.  

Yeah, I agree, it can all be avoided.  But, you gotta' admit it is all unneccesary and a broken-ness in C++.  The language tends to encourage this problem -- just as much as straight C or Basic encourages problems there.   My guess is millions, maybe billions of dollars in programmer hours go down the drain because of exactly this problem.

[ Parent ]

how would you do it differently? (none / 0) (#126)
by avdi on Mon May 13, 2002 at 06:44:04 PM EST

I'm genuinely curious.  Given the constraints on C++, how would you change it so that private members don't weight down compilation?  Obviously, in a dynamic language like Python or Ruby there's no need to calculate the exact bytesize of the class at instantiation, nor to verify proper usage of the class's interface.  C++ doesn't have these luxuries.  And Java certainly doesn't improve on C++'s situation in this regard... 'interface' is merely syntactic sugar, and the full class specifications of any instantiated objects must still be examined at each compilation of a source file.  So, how would you improve on C++ in this regard?

--
Now leave us, and take your fish with you. - Faramir
[ Parent ]
Fixing C++ (5.00 / 1) (#127)
by cathryn on Mon May 13, 2002 at 09:11:52 PM EST

Warning, I'm a programmer, and not a language designer, but I would say that the obvious thing would be to resolve 'sizeof' at link time.  Really, the module knows how big it is; it should be in charge of calculating its own sizeof, and it should only happen once.  (This is instead of how it works now where every module that accesses a module recalculates sizeof over and over again.)

I'm not linker-hip enough to know if this could be done with existing linkers.  But, that's my thinking.  I assume the linker would need to be able to resolve static expressions involving sizeof, and perform a few other tricks.  But, I can't think of any reason, off the top of my head, why this should be so hard.

[ Parent ]

Compatibility with legacy linkers (5.00 / 1) (#128)
by GoingWare on Mon May 13, 2002 at 09:54:47 PM EST

There's an awful lot that could have been done if compatibility with existing linkers wasn't an issue. For example, if you could directly represent type information in an object file you wouldn't need name mangling.

But for practical reasons, Bjarne Stroustrup decided to design C++ so that it could be linked with existing linkers meant for C. In general the only extra work required for a linker to support C++ was to increase the maximum length of a symbol, to support name mangling. Something I've always wanted to see (and that was considered by Stroustrup) is to store object code in a proper database. I believe there are systems where this is done. Then linking could be made very fast and it would be simple to support incremental linking.

The original C++ compiler that Stroustrup wrote, at&t cfront, was actually a C++ to C translater. It compiled your C++ source to some difficult-to-read C++ source, then you built that with your standard C compiler. That allowed C++ to be brought up on more systems quicker than if he wrote a machine code generator, but had the unfortunate side effect of making a lot of people thing it was just a fancy macro processor early on.


I am the K5 user now known as MichaelCrawford. I am not my corporation.


[ Parent ]

Careful. (none / 0) (#130)
by i on Tue May 14, 2002 at 01:21:26 AM EST

By hiding private parts from the compiler, you pretty much kill inlining of accessor functions. No good! That is, unless the linker can do inlining. At which point it just becomes a compiler.

Plus, you put 90% of template metaprogrammers out of business. They need sizeof available at compile time.

and we have a contradicton according to our assumptions and the factor theorem

[ Parent ]

inline -- I see the issue (none / 0) (#131)
by cathryn on Tue May 14, 2002 at 04:14:47 AM EST

I guess as I would envision it, the linker would have to do inlining also.  I don't see how that would necessarily be all that bad.  I assume, the compiler would build the inlines as part of the object file, and then when an inline is called, the linker would then be able to plug in the appropriate code, and repair the branches around the inline.  To me, this could arguably be described as a 'linker-like' function, rather a compiler-like function without opening up too big a can of worms.

I have to think about templates some more.  


[ Parent ]

It's doable, but... (none / 0) (#132)
by i on Tue May 14, 2002 at 04:38:13 AM EST

Optimisation usually happens after the inlining step. If you don't want to kill the optimisation, it too has to be done in the linker. At this point the linker starts to resemble the compiler even more. What if you want to support dynamic linking? Congrats, you have a full-blown JIT system.

and we have a contradicton according to our assumptions and the factor theorem

[ Parent ]
I see your point. (none / 0) (#133)
by cathryn on Tue May 14, 2002 at 06:03:06 AM EST

Obviously, if the goal is faster compiles, it's pointless to do optimizations in the link since then it'll run optimizations on the entire project for every build.  

I think the only way to resolve this would be to accept that with modularly linked inlines, there will be limitations to the optimizations, and if you absolutely need all the speed, you'll need to expose the inline routine and those variables needed for the inlines -- the way we do it now.  

If you want maximum runtime speed and best compile speed, maybe what you do is for the accessor inlines is expose just those variables and the inlines.  But, you'd still get the runtime speed advantage of not needing to do inheritance/virtuals or pimpl or something like that to keep the compile time down for the non-inlined stuff.  

The advantage being in the process of creating a large project, everyone would start with their privates being private.  Compile times stay fast, and, the naive use of the language would result in fast builds, and clean structured code, because of fast buildds.    And, then only at the end at the optimization phase, do you expose a bit of privates and inline some stuff here and there to speed it up.  But, only where it's really needed.  So, the mega-interdepency we're stuck with all the time, now gets applied in a bit more of a careful way.

Anyway, that's just a thought. I'm sort of making this up as I go along.  

[ Parent ]

You can do this right now. (none / 0) (#134)
by i on Tue May 14, 2002 at 06:41:19 AM EST

Just use abstract base classes throughout. At performance tuning time, swap some of them for their concrete implementations (in release builds only).

and we have a contradicton according to our assumptions and the factor theorem

[ Parent ]
yes (4.00 / 1) (#123)
by ucblockhead on Mon May 13, 2002 at 11:59:15 AM EST

I agree completely with that. This is the single worst feature of C++. Whereas you could have a nice scheme where interface is completely separate with implementation, you instead have a scheme where half the implementation is shoved into the interface.

And I think the Java/C++ template solution of simply doing away withg a seperate interface is pretty sucky, too.
-----------------------
This is k5. We're all tools - duxup
[ Parent ]

Hiding your privates (none / 0) (#141)
by ephelon on Fri May 17, 2002 at 03:30:23 PM EST

The reason for this, is of course, so that sizeof() will work.  The solution is to stick all of your privates somewhere else.

// Foo.h

struct FooPrivate;

class Foo
{
// stuff goes here
private:
  auto_ptr< FooPrivate > m_pPrivate;
};

// Foo.cpp

struct FooPrivate
{
  int some_member;
};

Foo::Foo()
  : m_pPrivate( new FooPrivate )
{

}

In this way the only thing contained in the class is the pointer to some other memory, and you can keep the structure private.

Disclaimer: I didn't try this, it's just off the top of my head.  I generally stay away from these sorts of tricks because it just makes my code messy.  Besides, I get the chance to read K5 while I wait for my project to build.  =)
-- This is not my home; the cats just let me stay here.
[ Parent ]

Musings on Good C++ Style | 145 comments (136 topical, 9 editorial, 0 hidden)
Display: Sort:

kuro5hin.org

[XML]
All trademarks and copyrights on this page are owned by their respective companies. The Rest © 2000 - Present Kuro5hin.org Inc.
See our legalese page for copyright policies. Please also read our Privacy Policy.
Kuro5hin.org is powered by Free Software, including Apache, Perl, and Linux, The Scoop Engine that runs this site is freely available, under the terms of the GPL.
Need some help? Email help@kuro5hin.org.
My heart's the long stairs.

Powered by Scoop create account | help/FAQ | mission | links | search | IRC | YOU choose the stories!