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 (26)

  1. DrPizza says:

    "STL isn’t the kind of library that I like to use, as I think it’s too baroque."

    It’s not clear to me that you’ve used the STL. It has a *few* too many methods (that is to say, there is some amount of functionality that’s duplicated and not really required). But to suggest that it’s in general excessive is completely idiotic; the majority of the methods it has are necessary for any semblance of completeness (and I will ignore any smart-arses who mention turing machines…).

    It certainly does things that .NET’s libraries don’t, but that’s because .NET’s libraries are deficient, not because C++’s libraries too bloated.

    "When I used const in my projects, I always ran into situations where I needed a routine that was const to become non-const."

    I’m hard-pressed to see a situation in which that could ever occur. Changing from const to non-const is a *huge* change in behaviour. What kind of situation could warrant such a change without also warranting at the very least a new method?

    "In C++, I’m currently dealing with code that uses:"

    It’s hardly C++’s fault that you’re using bad code. CString, for example, is pretty inexcusable. LPTSTR and TCHAR* are the same type, and are often the same type as WCHAR*, and sometimes the same type as _T(""); it seems rather disingenuous to mention them.

    " I also spend time making sure I have the right distinction between byte count and character count when dealing with such types."

    I think you meant to write "I am too stupid to use wrappers".

  2. Bryan says:

    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.

  3. Eric says:

    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.

  4. Jim Lyon says:

    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.)

  5. Erric Gunnersion put a post up on his blog where he talks about some of the things he likes about C#…

  6. DotWind says:

    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

  7. 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

  8. C++ has the ability to write functions that return a reference to a value. As far as I can tell, C# does not.

    Why does this matter? If means that while in C++ and the STL you can happily build a vector of values, and then modify things in place in that vector:

    vector<MyStruct> vec;

    vec.push_back(MyStruct(10,20));

    vec[0].X += 1;

    On that last line, "vec[0]" retrieves a reference to the item in the vector, so I’m modifying the item in the vector.

    C# arrays work like this two – the array accessor returns a reference to the thing in the array, not a copy, so I could do the equivalent:

    MyStruct[] vec = new MyStruct[1];

    vec[0] = new MyStruct(10, 20);

    vec[0].X += 1;

    But because in C#, methods don’t get to do this trick of returning a reference to a value, the same thing *doesn’t* work with a container class:

    List<MyStruct> vec = new List<MyStruct>();

    vec.Add(new MyStruct(10, 20));

    vec[0].X += 1; // Error!

    This fails because the List indexer returns a copy of the array element, rather than a reference to it. It’s good that the compiler points out that what I’m trying to do won’t work, rather than just silently modifying the copy, leaving me to wonder why that line of code appears not to work… But it’s annoying that I can’t do what I want, particularly since it works just fine when working with a raw array. (Or an STL vector…)

    As far as I can tell, you can’t build container classes that look like arrays in C#. There’s no way for the indexer of a collection class to let me work with the element that’s in the array in the same way that the indexer for an array of value types behaves. All a container can do is return me a copy.

    If I want to be able to modify items in a collection in situ, I’m forced to use reference types in C#.

  9. Radu Grigore says:

    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.

  10. DrPizza says:

    "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….

  11. 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.

  12. 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.

  13. 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.

  14. Mr. Dew says:

    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.

  15. Aleksey says:

    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++.

  16. 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.

  17. What about Turing machines

  18. Michael Bosley says:

    Please, don’t take what I’m about to say as me being mean or flaming you… it really isn’t intended that way. 🙂

    I’m just learning C#, but I’ve written in assembly, C, C++, Java, and a few other languages for the past 10ish years professionally.

    If you’re trying to write cross platform code, you’d be better off using Java. Unless, of course, it needs to be compiled.

    Using Microsoft’s types to try to point out deficiencies in C++ isn’t a good idea because those types aren’t really types at all.

    CString is an class (albeit an absolutely horrid one), so it follows your argument.

    LPCTSTR, LPTSTR, TCHAR*, WCHAR*, BSTR, _T("blah"), and TEXT("blah") all are either char* or unsigned short*, depending if UNICODE is defined or not. That’s it… if you follow the layer upon layer of defines, you’ll see what I mean.

    There is no such thing as "const if I don’t have a reason right now for it not to be const". Changing from const to non-const functionality means that you have a function that you’re trying to hack to do something totally different. For example, if you have a function that checks to see if a string has unprintables in it, then change it to change the unprintables to something, you must write a second function because the first now no longer accomplishes it’s goal. If you have to go back and change the const for all of the callers, you’ve essentially said "I have no idea what this is going to do…" because the functions still treat them as having been changed.

    Take, for instance:

    void do_stuff(const char *str)

    {

    if (has_unprintables(str))

    write_unprintables(str);

    else

    write_printables(str);

    save(str);

    }

    If has_unprintables suddenly changes to modify str, you’ve got huge problems. First, the "has_xxx" tells the developer that you want to know a yes or know, not that you want to change something. Second, saving the string now results in saving the potentially modified string, which changes your output.

    "assumes that you can do the design in detail up front"

    If you’re writing a function, you must know what purpose it is to serve. If it is ambiguous at the stage you need to write the function, then you don’t know what you’re supposed to be writing. Not trying to be mean, but programming is the art of unambiguously giving direction. If the requirements are unclear, development cannot begin… unless you want to just toss the code and start over later.

    Maybe I’m missing it, but how are events more difficult in C++ than C#?

  19. Michael Bosley says:

    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.

  20. Eric says:

    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.

Skip to main content