Making destructors compatible with C#...

Started by
18 comments, last by arbitus 12 years, 6 months ago
Hi all,

I rely somewhat heavily on destructors in my programming. So the question I have here is how I can simulate this in garbage collection languages like C#. I program primarily in C++, but am looking for things I can do to make an eventual port to C# easier.

For instance, say I have a skeleton class that always keeps a variable updated somewhere...

class Skeleton
{
Skeleton() {gSkeletonCount++;}
~Skeleton() {gSkeletonCount--;}
};


Now, each "new Skeleton" increases gSkeletonCount, and each "delete Skeleton" decreases gSkeletonCount, and I always know how many of my skeletons are alive.

To convert this to C#-ish (or Java) compatibility, my initial inclination is to do this:

class Skeleton
{
Skeleton() {gSkeletonCount++;}
Destructor() {gSkeletonCount--;}
};

#ifdef NOAUTOGARBAGECOLLECTION
#define Delete(Object) {Object->Destructor();delete Object;}
#else
#define Delete(Object) {Object->Destructor();}
#endif


Simple enough!

BUT, and here's my question...
How could I make this compatible with other aspects of my code? For instance, I can't always count on having Destructor() defined... so if I'm going to say new int and delete(int), I will have to have a version of my defined "delete" that does NOT call Destructor(). That sort of sucks... are there any high-end preprocessor operations that could get around that?

And, I'm not even sure how I could wrangle this:


Skeleton *aSkellies=new Skeleton[25];
delete [] aSkellies;


...how could I make a define for delete [] that would actually call the Destructor() on each item?

Thanks for any help you can give!
Advertisement
Managed languages aren't required to run garbage collector.

so if I'm going to say new int and delete(int)[/quote]
Managed languages don't have delete either.

The problem listed above is solved differently:class Skeleton {
List<Objects> skeletons;
};

// instead of having gSkeletonCount
Skeleton s;
s.skeletons.count();
Or, external resource management is discouraged, instead it's handled by container class.

I program primarily in C++, but am looking for things I can do to make an eventual port to C# easier.[/quote]
In short, nothing.

Programming models are fundamentally different. It will not make porting any easier and will only make C++ code a mess. There are simply too many requirements on C++ internals to make such port viable.


One brute force approach is:
- never define destructors
- never use references
- any pointer that is ever used anywhere may only ever be smart pointer (shared_ptr)
- never pass or store anything by value except for primitive types (int, float)

When writing such code the necessary idioms will emerge naturally, but it will also result in overly cumbersome and verbose C++ code, which will be considerably less efficient than equivalent managed code, so might as well dump C++. Such design also makes it impossible to use just about any third--party library, including STL or Boost.

are there any high-end preprocessor operations that could get around that?[/quote]
It's not syntax, the whole design is completely different.

It should also be noted that such trivial design has proven disastrous in Java and to lesser extent C# (due to huge pressure on GC and too much redundant resource management imposed by deep object graphs) which have adopted a different approach to resource management, a hybrid between extrinsic resource management and whatever language requires.
Resource management is different enough that a 1:1 port between a manually memory managed, value-oriented language and a garbage collected, reference oriented language would be inadvisable.

One option is to always use explicit functions for deallocation, and use the destructor to assert that the explicit function has been called, but it is far from a complete solution.
Honestly, your best bet is to give up on a copy/paste "port" and convert the program correctly - that is, by changing its design to suit the target language.

What you want to do is analogous to this:
  • You have a rocket
  • You have a car
  • You want a fast car
  • So you put the rocket inside the car
  • Now the rocket blasts through the windshield and destroys the interior of the car
  • You ask how to stop this from happening


The answer, of course, is to stop trying to put a rocket inside a car. Just fly the rocket, or just drive the car.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]


In short, nothing.

Programming models are fundamentally different. It will not make porting any easier and will only make C++ code a mess.
A very good point.
It's not worth compromising the quality of your C++ code... when you could just compromise on the quality of your C# code instead :P . I mean everywhere where objects would normally be destructed in the C++ code you could use a using statement in C#. I mean you could, not that it'd necessarily be a good idea.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
So, all philosophical dissertations and fatherly advice aside...

Does anyone know the mechanics of how I would accomplish either of the situations above?

I.E., 1, in the preprocessor, determine if the object I'm calling a Delete() macro on possesses a certain function that I can call (this one is probably impossible, but I have known some C++ gurus who have been able to come up with some astonishing preprocessor stuff)...

Or 2, when macroing delete [] into DeleteArray[] to actually step through each element and call a function?

So, all philosophical dissertations and fatherly advice aside...

Does anyone know the mechanics of how I would accomplish either of the situations above?

I.E., 1, in the preprocessor, determine if the object I'm calling a Delete() macro on possesses a certain function that I can call (this one is probably impossible, but I have known some C++ gurus who have been able to come up with some astonishing preprocessor stuff)...

Or 2, when macroing delete [] into DeleteArray[] to actually step through each element and call a function?

IDisposable is about as close as you're going to get, but even then it basically amounts to calling a method whenever you want your object to be disposed, and it's typically reserved for handling unmanaged resources. This is what iMalc referred to when he mentioned the using statement, since you at least get a little syntactic assistance. Others have already given you their share of why this isn't a great idea, so I won't go there :)

For instance, say I have a skeleton class that always keeps a variable updated somewhere...
[/quote]

That isn't really a good idea even in C++...


IDisposable is about as close as you're going to get,
[/quote]

Or you could have a separate method for termination that you need to call explicitly. That way you disentangle the logical 'killing' of an object from the memory management aspects of releasing the object's memory.
You could create a manager class for the skeleton objects which contains the list of skeletons and use that to control the lifecycle of the skeleton objects. Then to delete them you call Manager.Delete(skeleton) and you can include any custom code you want that would normally be in the destructor of the skeleton class.

That should be doable in both c++ and c# without having to mess with either language's design philosophy.

I think if you try to solve the problem with some complicated preprocessor magic, you're creating more work for yourself down the line.
[size="2"]Currently working on an open world survival RPG - For info check out my Development blog:[size="2"] ByteWrangler

So, all philosophical dissertations and fatherly advice aside...

Does anyone know the mechanics of how I would accomplish either of the situations above?

I.E., 1, in the preprocessor, determine if the object I'm calling a Delete() macro on possesses a certain function that I can call (this one is probably impossible, but I have known some C++ gurus who have been able to come up with some astonishing preprocessor stuff)...

Or 2, when macroing delete [] into DeleteArray[] to actually step through each element and call a function?


I may be misunderstanding your original question, but I would handle your specific scenario with a list. In your example, you seem to be mixing together object death with monster death. With a garbage collected language, object death isn't quite as important, so you're mostly concerned with the death of the monster.

I would just use the following:


List<Skeleton> skeletons = new List<Skeleton>();


If you want to get the skeleton count, use:

skeletons.Count;


To add a skeleton:

skeletons.Add(new Skeleton());


Delete a skeleton:

skeletons.Remove(skeleton);

Or:

skeletons.RemoveAt(index);


If you would like to remove all the skeletons in the list, use:

skeletons.Clear();


If there's something you want to do when a monster dies, you can create an OnDie() method or a Kill() method which your engine will call when a monster dies.
I apologize if I misunderstood your question.

Edit: Oh! I see now. You're looking for things you can do in your C++ code to make a possible future C# port easier. I apologize, but I'll leave my original response above just in case.

This topic is closed to new replies.

Advertisement