"Poll" Has C# Generally Replaced C++

C

Carl Daniel [VC++ MVP]

"Carl Daniel [VC++ MVP]" <[email protected]>
wrote in message
Still not apples to apples - how 'bout:

std::vector<int> x;

vs.

System.Collections.Generic.List<int> x = new
System.Collections.Generic.List<int>();

-cd
 
T

Tamas Demjen

Greg said:
e.g. int[] x; v.s. int x[];

int[] x;
vs
cli::array<int> x;

I agree, the original designers of C could have separated the type a
little bit better. But nobody uses types like int[] anymore for function
arguments -- you simply pass int*.
C# has the advantage of practical "non-rushed" thinking in its design.

And yet it doesn't support such basic features as constants and constant
member functions. No destructor (!!!) means no RAII, which is big
problem when an object holds a resource. You can't create a class on the
stack. No operator overloading, no templates. You can't even declare a
pointer and increment it. And so on, I could continue.

Tom
 
C

Carl Daniel [VC++ MVP]

JAL said:
This is as expected in a garbage collected runtime,

It's really not a consequence of the runtime being garbage collected, but
rather a consequence of the by-reference way in which objects are handled in
C# (or Java). The lifetime of the object is de-coupled from the lifetime of
the reference by the indirection. This can be a "Good Thing" if the object
contains no resources other than memory, but it's a detriment when the
object holds other kinds of resources.
but you can still do deterministic cleanup:

http://www.geocities.com/Jeff_Louie/oop26.htm

Yes you can. The problem is, you have to remember to do it. In C# it's a
coding idiom. In C++ it's the natural language semantics.

-cd
 
T

Tamas Demjen

JAL said:
This is as expected in a garbage collected runtime, but you can still do
deterministic cleanup:

http://www.geocities.com/Jeff_Louie/oop26.htm

I don't want that. try/catch can't do

std::vector<boost::shared_ptr<Resource> > items;

I need my destructor, not only for deterministic cleanup in a scope, but
also for deterministric cleanup in containers. If you store resources
that require destruction (non-memory-only resources) in a container, you
still want to make sure that when the container goes out, all its items
go out automatically. Maybe if you program in a fully managed
environment you don't use destructors that often, but I essentially wrap
unmanaged code to managed interfaces, and therefore all of my classes
requires Dispose() to release unmanaged memory in a timely manner. You
could say they'll eventually be garbage collected, but I'm saying it's
too late. Some resources are very critical and must be destroyed
deterministically, and unmanaged memory must be deallocated as soon as
possible too.

Just imagine storing high resolution color images wrapped in a very thin
managed class. I don't care if the garbage collector doesn't kick in for
a while for the 16-byte unmanaged part, but my 50 MB unmanaged data had
better be deallocated when they're not used anymore. I can't wait until
the garbage collector runs out of managed memory, because my unmanaged
memory will run out much earlier. My example shows that a single missed
call to Dispose can be just as catastrophic in the managed world as it
is in the unmanaged world, except the unmanaged world has tools
(boost::shared_ptr) to deal with that situation. And I believe C++/CLI
is prepared to deal with those cases too. C# is not. Correct me if I'm
wrong.

You could say I'm just spoiled by modern C++ and boost, but C# is a step
behind from this point of view, and .NET itself is a step behind for not
supporting const correctness too. It's also a step forward for
properties, events, reflection and self-contained packaging (managed
assembly) support.

Tom
 
A

Andre Kaufmann

Greg said:
Is it your general opinion that C# is generally designed/intended/ready to
replace C++? I know the answer is not black and white but please repond YES
or NO (add any comments if you would like)

I just thought it might be interesting to get a tally. This is related to a
previous thread. So far we have two NOs from "aa" and Bo Persson.

Yes and no.

IMHO C++ - C++/CLI and C# can be used very effectively together and both
languages have their pros and cons.

What about if the C# compiler could freely mix both languages in a
single project e.g. something like 'extern "C++" in C# ;-) and directly
call the C++/CLI code without using an intermediate DLL or linker tricks ?

If additionally C# would have RAII I would be perfectly happy about this
combination.

To be real, I think both languages will coexist for a very long time.
Only if the basis, the operating system and it's main interfaces will be
managed code too and if the native MSIL compiler will be available I
think more and more code will be written in plain C#.

Currently I think C# has another focus than replacing C++. But this may
happen some day.

Andre
 
G

Guest

int[] x;

x has all the methods of ICollection and Array. So I think int[] x is apples
to apples.
 
G

Guest

Then you should like C++/cli which implements the using construct within the
language semantics as long as the ref class is constructed "on the stack".
 
G

Guest

I don't really understand your point. Java and C# are garbage collected and
objects are created using reference semantics.They do not normally use
reference counting so finalization is not deterministic. Seems to me there is
a direct link between the decision to use garbage collection and the lack of
deterministic finalization.

C# value types do not have destructors, but C++/cli ref classes can be
declare "on the stack" so that the "destructor" gets called when the "value"
goes out of scope, much in the way that delete is called on a contained
pointer when the smart pointer goes out of scope.
 
G

Guest

No... but smart pointers replace the try catch also

void foo()
{
auto_ptr<MyClass> p(new MyClass);
p->DoSomething();
}

replaces

void foo()
{
MyClass* p;
try {
p = new MyClass;
p->DoSomething();
delete p;
}
catch (...) {
delete p;
throw;
}
}

:
I don't want that. try/catch can't do

std::vector<boost::shared_ptr<Resource> > items;
 
A

Andre Kaufmann

JAL said:
I don't really understand your point. Java and C# are garbage collected and
objects are created using reference semantics.They do not normally use
reference counting so finalization is not deterministic. Seems to me there is
a direct link between the decision to use garbage collection and the lack of
deterministic finalization.

Regarding memory this is true. But regarding resources (e.g. file
handles) you cannot always rely on garbage collection.

You have to write:
void foo()
{

using ( File f1 = new File(), File f2 = new File())
{
....
} <-- Here the files are automatically closed

}

Would be fairly easy to allow using keyword to be used in a more direct
manner in C#:

void foo()
{
using File f1; // Automatically default constructed
using File f2("aa"); // Automatically constructed
} <-- Here the files would be automatically closed



But this unfortunately doesn't help if you need to have an object in
multiple lists and automatically disposed after the last reference has
been removed from the list.

Andre
 
A

adebaene

Greg said:
int[] x;

x has all the methods of ICollection and Array. So I think int[] x is apples
to apples.

An array in C# is not extensible. A std::vector is....

Arnaud
MVP - VC
 
C

Carl Daniel [VC++ MVP]

JAL said:
I don't really understand your point. Java and C# are garbage
collected and objects are created using reference semantics.They do
not normally use reference counting so finalization is not
deterministic. Seems to me there is a direct link between the
decision to use garbage collection and the lack of deterministic
finalization.

Deterministic object lifetime and deterministics memory reclamation are two
separate things. C# joins them into a single concept, but they needn't be
so joined. C++/CLI is an example of a language that treats them as the
separate things that they are.

-cd
 
B

Bo Persson

JAL said:
No... but smart pointers replace the try catch also

void foo()
{
auto_ptr<MyClass> p(new MyClass);
p->DoSomething();
}

replaces

void foo()
{
MyClass* p;
try {
p = new MyClass;
p->DoSomething();
delete p;
}
catch (...) {
delete p;
throw;
}
}

Or, if you are using proper C++:

void foo()
{
MyClass m;
m.DoSomething();
}


No problem! ;-)


Bo Persson
 
T

Tamas Demjen

JAL said:
No... but smart pointers replace the try catch also

My point is that the C# using keyword works for trivial cases when you
locally allocate and object and delete it right away. But C# using
doesn't work when the object has a longer life span, but still requires
automatic destruction. How do you store a list of resources in a
container/collection with deterministic cleanup?

All you're showing is that

C++ auto_ptr<T> t(new T)
is the same as
C# using(T t = new T)

That I agree with. But the real differences begin to show up when I can
do in C++ vector<shared_ptr<T> >. In C# I don't think there's a solution
to it. I actually tried to put objects into a List<T> using C# 2.0, and
it didn't automatically call Dispose on T. So List<T>::Dispose (if it
has such a thing at all) doesn't call Dispose for the contained objects.
I think it would be nice if .NET collections had a Dispose method, which
would call Dispose for its members. But even that wouldn't solve a lot
of other problems that reference counted shared_ptr does. What if a
single resource has 2 object copies, both handling the same resource,
and the actual resource should only be disposed when the last object
copy goes out of existence?

As Carl said, pure managed memory reclamation is not the same thing as
resource reclamation. In C# you don't worry about releasing allocated
managed memory, but you still have the burden of reclaiming resouces,
and the language doesn't provide a very good support for that. The using
keyword only solves that for trivial cases, when the object is create
and delete in the *same scope*, which can't always be ensured. In
complex applications resources are stored in containers, in other
objects, and they're destructed in a very complex way.

One more advantage of the boost implementation is weak_ptr. It clearly
separates ownership semantics (shared_ptr) from reference semantics
(weak_ptr). Using weak_ptr you can be sure that you don't leave trash
objects behind accidentally. It automatically makes sure that when the
last shared_ptr goes out of existence, all weak_ptr's that refer to the
object get NULLed out automatically. I challenge you to implement this
in C#, without manually having to call Dispose. I believe it's doable in
C++/CLI, even if we don't initially have such a solution yet.

I'd like to note that even in C++/CLI the standard .NET containers
suffer from deterministric destruction problems:

using namespace System::Collections::Generic;

ref class Guarded
{
public:
~Guarded() { Console::WriteLine(L"~Guarded"); }
};

int main(array<System::String ^> ^args)
{
List<Guarded^> items;
items.Add(gcnew Guarded);
return 0;
}

Although "items" uses stack semantics, when it goes out of scope it
doesn't Dispose its members. And trying to do

List<Guarded> items

doesn't work, because List<T> is a generic, not a template. I'm not sure
if the upcoming STL.NET will solve this. It's very concerning when I
program in .NET, even in C++/CLI. I simply can't use the .NET containers
to store objects that wrap unmanaged types, because the Dispose pattern
is broken. I think it's just a matter of time before really good C++/CLI
container implementations come out, where we'll be able to write

vector<shared_ptr<ManagedClass> >

Not having the proper tools for this problem, I feel like I live in
danger, just like C programmers do (whether they admit it or not). Or
even more so, because C programmers are not used to exceptions, but .NET
programmers must think of exception safety. Every system that provides
exceptions but no exception safety is a disaster waiting to happen.

Tom
 
G

Guest

Some have pointed out advantages of C++ which I don't contest. Yet, I am not
sure these are outweighed by other considerations for many .NET development
scenarios.

There are some cases where C++ is the only option: e.g. a pure native
Windows App with no .NET Runtime required.

However for a .NET app, if C++ is on par with C#, then how does one decide
which language to use. Are there any criteria besides being already familiar
with C++ etc.?

I.e. are there any concrete reasons why the full development cycle right up
to delivery will be more cost effective and yield greater ROI using one
language vs. the other?

Thank You
 
G

Guest

Hi Tom... I agree that using comes up short when compared to smart pointers.
Using is "... about as close as we can get to a smart pointer and RAII in C#"
The best I can do in C# is:

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace DeterminedCollection
{
interface IData
{
int I { get;}
}
interface IMyInterface : IData, IDisposable{}
class Dummy : IMyInterface
{
private bool disposed = false;
private int i;
public Dummy(int i)
{
this.i = i;
}
public void Dispose()
{
Dispose(true);
Console.WriteLine("disposed");
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing) // called from Dispose
{
// Dispose managed resources.
}
// Clean up unmanaged resources here.
}
disposed = true;
}
~Dummy() // maps to finalize
{
Dispose(false);
}
public int I {
get {
if (disposed) { throw new ObjectDisposedException("Dummy"); }
return i;
}
}
}
class JALCollection : IDisposable //, IEnumerator
{
private bool disposed = false;
private ArrayList list = new ArrayList();
// ASSERT d is not null
// ASSERT no object holds a reference to
// d outside of this class
// USAGE Add(new MyClass());
// where MyClass implements IMyInterface
public void Add(IMyInterface d) {
if (d != null)
{
list.Add(d);
}
else { throw new ArgumentException(); }
}
// test only, not safe! implement IEnumerator
public int GetValue(int i)
{
return ((IData)list).I;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
foreach (IDisposable d in list)
{
d.Dispose();
}
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing) // called from Dispose
{
// Dispose managed resources.
}
// Clean up unmanaged resources here.
}
disposed = true;
}
~JALCollection() // maps to finalize
{
Dispose(false);
}
}
class Program
{
static void Main(string[] args)
{
using (JALCollection jal= new JALCollection())
{
jal.Add(new Dummy(1));
Console.WriteLine(jal.GetValue(0)); // test only
}
Console.ReadLine();
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top