More on C# and C++
Thanks for all the comments - I'd like to expand on a few of them.
Cross-Platform
When I wrote the last post, I was thinking of cases where you have a choice between languages, so I didn't think of cross-platformk, since if you need to run on a platform where a language isn't present, that pretty much eliminates the language from consideration.
It's true that C++ is available on far more platforms, and if that's important in your case, C# probably isn't an option for you.
Templates, template metaprogramming, STL, Boost
I missed templates as an advantage that C++ currently has, as I forgot that Whidbey isn't there yet for C# programmers. When Whidbey is widespread, C# will have the majority of the features that I'd want related to generic types, though it won't be able to do as much as C++ does.
In my mind, that's (mostly) a good thing. While there are things that aren't in C# generics that I'd like, I think that, because of the indirection involved, generic types are something that are best enjoyed in moderation, as they're near the limit of what most programmers can easily understand. Which brings us to template metaprogramming. The discussions I've read on this topic list "power" and "optimization" as the big advantages of this technique, and I'd have to agree with that evaluation. But the code that I've looked at makes normal template code look simple and straightforward. So, I'm not sorry that you can't do this with C# generics.
Something I do miss is the ability to do Mixins, which would be a nice complement for a language without multiple inheritance. They would be helpful to add in system functionality without burning the base class.
STL isn't the kind of library that I like to use, as I think it's too baroque. Sure, you can do a *ton* of things with it and easily switch things around, but I've never found that I need to switch things around that often, so it's complexity that I don't use, but still have to deal with. So, for me, no thanks - I'd rather have foreach, which covers about 90% of my loops. Oh, and before I leave this topic, I should mention that the richness of data structures in STL is a lot greater than that in C#, though you should keep your eye out for C5 and PowerCollections when Whidbey shows up.
In the current C#, foreach only supports one way of iterating. About 3 years ago I wrote an article on some collection wrappers you could use to support other ways of iterating, though at some cost to performance. Unfortunately, I chose to call the "iterators", which, of course, is also the name of a C# 2.0 feature that allows you to make objects iterable more easily, and support multiple ways of iterating a collection.
Boost seems like an obvious C++ advantage, if you're working in an environment where you can use outside libraries.
Object Lifetime
There were a lot of comments around deterministic destruction, and there is certainly a big difference between the "programmer owns the allocations" and the "the GC owns the allocations" approaches.
I will admit that when I first started using C#, I missed that feeling that I had full control over what was going on in the system. But over time I found that while I did need to be concerned with scarce resources (db connections, file handles, and other system resources), I didn't really need to be spend a lot of attention on memory resources. For scarce resources, "using" works well for me, and I prefer the scoping that "using" gives me over the scope-based lifetime that you get with smart pointer approaches in C++, and I also like that it's more explicit.
This does not mean that you can totally ignore the issues around object allocation in C#, as Rico's has said, repeatedly.
Oh, one other point on object lifetime. Having an environment where there is no automatic scope-based lifetime makes supporting exceptions much cheaper in C#, as there isn't the overhead of tracking what objects are live at any point that is required by C++ exceptions.
const
Which brings us to const. My experience with const is as follows:
When I used const in my projects, I always ran into situations where I needed a routine that was const to become non-const. That meant either changing that routine - and then updating all of the callers so that they were non-const - or creating non-const versions of existing routines where applicable. Neither of those is a particularly nice and/or fun thing to do, and after trying it for a while, my conclusion was that having const didn't give me enough benefits to make it worth the disadvantages.
I do agree that const can give some protection against the programmer doing the wrong thing (which, interestingly, is not really in keeping with the general C++ philosophy that programmers should be able to do whatever they want, even if it's wrong (yes, I'm being a bit extreme there)), but since it's merely a convention and not a guarantee (as I can cast const away whenever I want, or just use "mutable"), I don't see a lot of value.
I've talked to enough people to know that my opinion is not shared by all.
C# things I missed
There were a couple of notable things I missed from my C# list.
Events
Events are much much more useful than I had originally thought. While you can do a lot of similar things with interfaces, events are great for the sort of loosely-coupled components that I like to create. For me, events are a feature that work exactly the way I want them to.
Data types
This is a big one that I missed.
In C#, there is one string type.
In C++, I'm currently dealing with code that uses:
- CString (the ATL/WTL type)
- LPCTSTR
- LPTSTR
- WCHAR*
- TCHAR*
- BSTR
- _T("constant")
and needs to transform strings from one type to another fairly regularly. I also spend time making sure I have the right distinction between byte count and character count when dealing with such types.
Comments?
If I've missed any that you'd like me to comment on or you have others to talk about, feel free to pile on...
Comments
- Anonymous
January 27, 2005
The comment has been removed - Anonymous
January 27, 2005
Despite DrPizza's bad attitude he does make a few good points. I'd like to personally state that if you need to change something from const to non-const you have seriously miscalculated the use of your library. That there is an indication of bad design, not a failing of the language. Constant parameters are a very powerful and useful technique and should not be thrown out with the bathwater just because you've misused it in the past. - Anonymous
January 27, 2005
Dr. Pizza,
On the subject on const and non-const, while I don't think such changes are common (in the sense that they happen every day), if I followed the "const if I don't have a reason right now for it not to be const" approach, when I added new capabilities the design needed to morph to support those new capabilities. Creating a new method was something I did at times, but then I had two methods with very similar behavior, which is bad.
On the subject of string types - In my original post, I noted that I was talking about the whole environment of C++ programming, and in the codebase in which I currently work, that's the environment I have.
It is true that somebody starting from scratch would be able to make choices that would hide at least some of that ugliness from view, but I don't think you can hide it all.
Bryan,
I understand your point, but I think it assumes that you can do the design in detail up front, which hasn't been my experience - there have always been changes along the way.
Like checked exceptions in Java, const seems to generate very polarized opinions, with people feeling strongly one way or the other. If you find that const is valuable for you, then you will obviously keep using it. As I indicated in my post, that hasn't been my experience. - Anonymous
January 27, 2005
Not only is it liberating in C# to not have to (usually) worry about when memory gets freed, it's also liberating that there's only one way to allocate/free memory.
By contrast, I remember working on one C++ project where I needed to repeatedly do things like copy strings from SHAlloc'd memory to CoTaskMemAlloc'd memory, or from CoTaskMemAlloc'd memory to GlobalAlloc'd memory. It was especially hard to guarantee that I'd done it right, because in all but a few bizarre corner cases, these are all really the same memory allocator. (It's also ironic that new'd memory was conspicuous by its absence.) - Anonymous
January 27, 2005
Erric Gunnersion put a post up on his blog where he talks about some of the things he likes about C#... - Anonymous
January 27, 2005
I remember when I first learned C++ it was with MFC and the think that I loved the most was the templates. Unfortunately, I don't have access to the beta version of whidbey yet to have that back. It will save me from generating those collection classes that I have in all my DALs - Anonymous
January 27, 2005
Eric,
the one thing i'm missing most and allmost every single day are 'real' macros (any other replacement mechanism for the c++ preprocessor will do.).
i assume this will get better soon as i can actually use generics, but it's a long wait.
WM_MY0.02$
thomas woelfer - Anonymous
January 27, 2005
The comment has been removed - Anonymous
January 27, 2005
It's a bit strange to hear that STL is too baroque. In my experience it is very easy to use because it is based on a few primitives that have consistent semantics -- almost independent of the context.
For example push_back menas the same thing for all sequences (vector, deque, list).
I do agree that C++ is generally too complex but STL represents a good usage of templates and is a well designed library precisely because its interface is easy to remember. - Anonymous
January 27, 2005
"It's a bit strange to hear that STL is too baroque. In my experience it is very easy to use because it is based on a few primitives that have consistent semantics -- almost independent of the context. "
No kidding. It's a trait sorely lacking in the "equivalent" .NET libraries.
"On the subject on const and non-const, while I don't think such changes are common (in the sense that they happen every day), if I followed the "const if I don't have a reason right now for it not to be const" approach, when I added new capabilities the design needed to morph to support those new capabilities."
Your decision to morph accessors into mutators is hardly an indictment of const. Like I said before, it represents a massive change in program behaviour; I've never found myself thinking "hm, I want to change this method in a small enough way that I don't want to add a new method, but in a big enough way that I want to make it mutate the object". It's just not something I've ever experienced, and I'm at a loss to think of a legitimate reason to do it.
"Creating a new method was something I did at times, but then I had two methods with very similar behavior, which is bad. "
Er.... If the behaviour of one method is to change the object and the other is to leave it unchanged I'd suggest that they're not that similar at all. And since they are different, why is having two methods bad? It seems rather good to me.... - Anonymous
January 27, 2005
I would like to see some of the things in C++ missing from C# added.
Indexers that return references seems like a small change to the CLR and language, and it would make a very productive difference for everyone creating value-type collections. Ian did you add this to the ladybug? I will vote for it.
Perhaps harder would be to reduce the startup overhead and memory consumption of the CLR when "doing nothing". This would enable us to use managed code more for simple console applications that do not need many library references.
I would like a way to ship ngened code instead of MSIL for things like Windows CE, where the JIT overhead and dual instruction representation (MSIL and native) are a real problem.
Finally I would like to see the results of JITing cached across application invocations, instead of having to use ngen, and I would like a more agressively optimizing JITTER, amortizing the extra time not across a single run of the application, but across sucessive runs (incremental optimization).
All of these things would improve the standing of managed code in relation to native code. - Anonymous
January 27, 2005
By the way Eric, the multitude of string representations is a Windows ugliness, and has nothing to do with C++. On other platforms it is not so ugly. - Anonymous
January 27, 2005
<p><ul><li><a href="http://www.denisbauer.com/NETTools/SQL2005Browser.aspx">Reflector.SQL2005Browser</a></li><li><a href="http://blogs.msdn.com/robcaron/archive/2005/01/27/361414.aspx">VSTS Dec - Anonymous
January 28, 2005
I think with Visual Studio 2005, there isn't going to be the gap between C# and C++ that there is now. C++ gets C++/CLI, C# gets generics. Sure, I still don't have foreach() in C++ and no "const" or templates in C#.
But the question becomes, how many C-like languages does Microsoft need to support? Sure, C# (and J#) are simplier than C++, and I don't like all the warts that C++ has either.
But if my reason for moving to C# from C++ is to get an easier language, why don't I go to VB.NET instead? Again, I'm talking VS 2005 where there will be far fewer differences beetween C++ and C# than there are today.
Sure, C# is going to look a lot nicer than VB.NET to C++ programmers, and no "real" programmer wants to use a "toy" language like VB.NET. But in reality, some syntax is about the only substantial difference between C# and VB.NET.
Don't get me wrong, I like both C++ and C# and don't want to use VB.NET. - Anonymous
January 30, 2005
VB.NET is more verbose, which some developers prefer. It's a matter of preference. I think VB.NET is great in the ASP.NET. - Anonymous
January 30, 2005
Great, Eric. On the second iteration you are more balanced. Though it clear that you don't have a good understanding of C++ in some areas. It's wise for you do admit that C++ has more power sometimes than C#. You named STL, but didn't delve into Boost, since you likely have no experience with it. I can assure you, deeper understading of Boost can enlighten you on many things. Things you didn't think were possible in C++. - Anonymous
January 31, 2005
Frank Hileman wrote: "Indexers that return references seems like a small change to the CLR and language"
I'm not sure that's true - it seems like a pretty major new language feature, since it enables a whole raft of scenarios that you just can't use today. And also a whole new set of problems. I've put up the example of someControl.Size.X = 42; a couple of times recently. Adding support for returning references to values would enable this to compile, but wouldn't necessarily be useful - how would the control get to discover that things have changed? (It also presupposes that the internal representation looks the same as this externalized version.)
And as for the CLR level - I'm really not sure about that one. Wouldn't it depend on whether the verification rules already support this? If they don't, then that's a very big change, surely? (And if you don't care about verification, you can already do this today with unsafe code in C#, using pointers.)
For references-as-return-values to be verifiable, they couldn't use the same rules as by-reference parameters. The only way that returning a reference could possible be verifiable was if you could restrict it to be an interior pointer only. This restriction doesn't exist for by-ref parameters, because the pointer is only valid for the duration of the call, meaning it's OK for it to refer to a location on the caller's stack.
So it's not obvious to me that it's necessarily all that straightforward. - Anonymous
February 10, 2005
What about Turing machines - Anonymous
February 16, 2005
The comment has been removed - Anonymous
February 16, 2005
Correction...
"If you're trying to write cross platform code, you'd be better off using Java. Unless, of course, it needs to be compiled."
When I said cross platform, I was thinking Windows/Linux cross platform, not PocketPC/Windows cross platform programming.
I think what I was trying to say is a "right tool for the job" comment, not "dude, just use java!" like it came across. - Anonymous
February 16, 2005
Michael,
1) In the original post, I noted that I was talking about the difference between environments, not merely between languages. I agree that a lot of the ugliness I deal with is from the Win32 (and other) APIs that I work in.
2) On "const", I've had several sets of comments telling me I was using it wrong, or didn't have a good design, etc. If others like it, that's fine, but for the way I like to develop, I haven't found const to be worth the cost.
3) Why are events easier? Well, having strongly-typed delegates help, having them contain invocation lists rather than single methods helps, having them work for either static or instance methods helps, having a cleaner syntax helps, as does having a design pattern for how you use them. All those add up to something that's a fair bit easier to us than C++, but it's not a giant change.
4) On cross-platform, C# isn't an option (Mono notwithstanding), so I don't think it makes sense to compare them in that context.