PDA

View Full Version : Idea for new language construct



Magos
09-22-2006, 05:27 AM
Many times when writing functions/methods I've wanted some kind of function-destructor concept. A piece of code that always runs when returning no matter where you're returning from. Of course similar effects can be done using smart pointers, additional function calls etc... but it'd be nice to have an easy-to-use buildt-in construct for this.

The basic structural idea:


int Func()
{
first
{
int* x = new int(1337)

if(Something()) return 0
if(SomethingElse()) return 0
if(YetSomethingElse()) return 0

return *x
}
then
{
delete x
}
}

The function-destructor is called after the return value has been pushed to the stack so the dereference (*x) will be safe. You won't be able to use return-statements in the destructor as it would screw things up.

Comments? Do you think this would make a useful addition?

Prelude
09-22-2006, 06:26 AM
>Comments? Do you think this would make a useful addition?
Something like a finally block for functions? I honestly don't see the need for it when you can achieve the same effect with RAII and a little planning.

Mario F.
09-22-2006, 06:56 AM
I'm not sure Magos.

Some compilers already implement that through function attributes, if I recall correctly, and that is probably how I would like best to have it implemented as a standard if it ever came to see the light of day.


void foo(){ /*... */ } //function definition

void foo() __attribute__((destructor()) { /*... */ } //function destructor definition


I think that would better match how destructors and constructors are used in classes. A first-then statement is perhaps too surprising since the flow of the code is afffected in ways you haven't seen before in C++.

CornedBee
09-22-2006, 09:52 AM
I agree with Prelude. The try...finally construct in Java and C#, which does what you want, is a crutch developed to make up for the lack of deterministic object destruction.

jlou
09-22-2006, 10:57 AM
int Func()
{
struct cleaner
{
cleaner(int* x_) : x(x_) {}
~cleaner() { delete x; }
int* x;
} c(new int(1337));

if(Something()) return 0;
if(SomethingElse()) return 0;
if(YetSomethingElse()) return 0;

return *c.x;
}Obviously this is the same as using a smart pointer, but I think new additions to the language will make these kinds of thing more streamlined and possibly as easy to use as a finally block for more indiviual and unique cases.

Magos
09-22-2006, 11:25 AM
As I mentioned smart pointers *could* be used instead, but to give a sligtly different example:


bool PerformTasks()
{
first
{
if(!Task1()) return false;
if(!Task2()) return false;
if(!Task3()) return false;
if(!Task4()) return false;
if(!Task5()) return false;
if(!Task6()) return false;

return true;
}
then
{
Cleanup();
}
}

Without duplicating the call to Cleanup, without having hugely nested if-statements and without having a huge if-test I see no clean and efficient way of performing this task.

CornedBee
09-22-2006, 11:29 AM
bool PerformTasks()
{
struct sentinel { ~sentinel() { Cleanup(); } } s;

if(!Task1()) return false;
if(!Task2()) return false;
if(!Task3()) return false;
if(!Task4()) return false;
if(!Task5()) return false;
if(!Task6()) return false;

return true;
}

And the code isn't even more indented (something I don't like about try...catch).

VirtualAce
09-22-2006, 01:19 PM
You don't need a finalizer if you can manage memory properly.

maxorator
09-22-2006, 01:48 PM
Doesn't C++ free memory under non-static variables in functions???

Magos
09-22-2006, 04:38 PM
The idea here was to suggest a construct that *might* or *might not* be useful in certain situations, simplify syntax and/or as an alternative. As an example in C++ you can do:


int a, b, c;

Though entirely pointless as you can just do:


int a;
int b;
int c;

It's still there for basically the same reasons as above. And for the record I'm not trying to claim my construct is the one truth. I asked for opinions, and I got them. Thanks.

anonytmouse
09-22-2006, 05:17 PM
As I mentioned smart pointers *could* be used instead, but to give a sligtly different example:


bool PerformTasks()
{
first
{
if(!Task1()) return false;
if(!Task2()) return false;
if(!Task3()) return false;
if(!Task4()) return false;
if(!Task5()) return false;
if(!Task6()) return false;

return true;
}
then
{
Cleanup();
}
}

Without duplicating the call to Cleanup, without having hugely nested if-statements and without having a huge if-test I see no clean and efficient way of performing this task.
In C (and C++, not using RAII (http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization)), the "goto cleanup" pattern is commonly used:


bool PerformTasks()
{
bool fSuccess = false;

if(!Task1()) goto cleanup;
if(!Task2()) goto cleanup;
if(!Task3()) goto cleanup;
if(!Task4()) goto cleanup;
if(!Task5()) goto cleanup;
if(!Task6()) goto cleanup;

fSuccess = true;

cleanup:
Cleanup();
return fSuccess;
}

Example (http://cboard.cprogramming.com/showthread.php?t=77802&highlight=goto+cleanup)

Your construct would essentially provide a nice syntax for this pattern. In C++ RAII is considered ideal, while C has been using this pattern for decades, so there is unlikely to be much support for introducing a new construct in either language.

Kennedy
09-22-2006, 06:15 PM
Doesn't C++ free memory under non-static variables in functions???
I don't understand exactly what you mean by this, but if this was the case, then you wouldn't be able to dynamically define an array under a method without having it vanish.

If you mean does it clean up for you on close, the OS *may* do this for you, however, I remember the days when one would have to reboot her/his computer because some POC program ate memory and didn't free it.

Mario F.
09-22-2006, 06:50 PM
For each new there must be a delete. Always.

VirtualAce
09-22-2006, 10:49 PM
..and each malloc a free.

It's actually quite simple.

maxorator
09-22-2006, 11:11 PM
For each new there must be a delete. Always.
I never use new ;)

jlou
09-22-2006, 11:22 PM
Doesn't C++ free memory under non-static variables in functions???
It sounds like you're talking about local variables. The answer is yes, they are cleaned up automatically. That is why RAII works, and how CornedBee's and my solutions are preferred in C++ over the idea of a finally block.

Dynamic memory, file handles, socket connections and many many other things are not cleaned up automatically. That is why you use RAII.


For each new there must be a delete. Always.Maybe, but most of the time you let your smart_ptr handle the delete for you. ;)

twomers
09-23-2006, 07:54 AM
>> Maybe, but most of the time you let your smart_ptr handle the delete for you.

Don't they just do the same thing? But it's hidden in the destructors :p

Mario F.
09-23-2006, 08:35 AM
RAII is of course the solution given what features the language present us at the moment.

But really no one came up with a good argument yet as to why function destructors are not a good idea. In my limited knowledge I don't see anything absurd in this addition to the language.

And if the best one can come up with is "you have RAII for that", then really most of the current language features would have to be removed since they too present coding alternatives. I just don't think that is a valid argument.

maxorator
09-23-2006, 10:25 AM
But the only variable or pointer that actually needs deleting after the program returns, is the one that is returned. Is deleting one variable worth the mess?

jlou
09-23-2006, 10:33 AM
Don't they just do the same thing? But it's hidden in the destructors :pUm, yes. :)


RAII is of course the solution given what features the language present us at the moment.

But really no one came up with a good argument yet as to why function destructors are not a good idea. In my limited knowledge I don't see anything absurd in this addition to the language.

And if the best one can come up with is "you have RAII for that", then really most of the current language features would have to be removed since they too present coding alternatives. I just don't think that is a valid argument.Aren't function destructors equivalent to the finally block?

And "you have RAII for that" is a valid argument because it is as good or better than the proposed alternative. Most of the current language features are improvements upon the alternatives.


But the only variable or pointer that actually needs deleting after the program returns, is the one that is returned. Is deleting one variable worth the mess?
That is just an example. Real world applications can be a lot more complicated.

maxorator
09-23-2006, 10:42 AM
But even real world applications can't return multiple variables. All the other variables can be deleted just before return.

jlou
09-23-2006, 11:02 AM
But even real world applications can't return multiple variables. All the other variables can be deleted just before return.
The point is that you can leave a function in many different places due to multiple return statements and/or exceptions. All of the resources that must be cleaned up must be cleaned up when the control leaves the function. Making sure that they are all cleaned up no matter what is the task that is made easier by RAII in C++ or the finally block in other languages.

Mario F.
09-23-2006, 11:24 AM
And "you have RAII for that" is a valid argument because it is as good or better than the proposed alternative. Most of the current language features are improvements upon the alternatives.


RAII is not a language feature. It's a coding paradigm. auto_ptr, which is a language feature, is just one way RAII can be implemented. And not always the needed or best method.

What was presented here was a new language feature that could indeed ease coding since RAII needs to be implemented by the coder either through an existing library or coding it themselves. It also forces the user to learn about RAII, how to implement it, the different possible implementations, and why and what to choose to implement.

In an effort to simplify the language, RAII is not the answer (albeit I'm not arguing against the merits of this paradigm). It only further complicates it. And you ask that to any newcommer to the language. Especially someone coming from languages which offer garbage collectors.

So... no. I still can't seem to agree with it. "You don't need it because you have RAII" seems to me much like saying "You don't need new shoes because your torn old ones still have a sole"

Prelude
09-23-2006, 11:55 AM
>What was presented here was a new language feature that could indeed ease coding since
>RAII needs to be implemented by the coder either through an existing library or coding it themselves.
The new feature also needs to be implemented by the coder. As I understand it, the suggestion was equivalent to a finally clause that has to be added to every block you want cleanup done on, regardless of what happens in the block itself. All of the grunt work is still done by the programmer.

>It also forces the user to learn about RAII, how to implement it, the different possible
>implementations, and why and what to choose to implement.
As opposed to learning about the new feature, how to use it, when to use it, and the pitfalls that it might have?

>In an effort to simplify the language, RAII is not the answer
If you want a simpler language, Java is there. :) C++ is as simple as possible for the power it offers. The result is certainly not a simple language, as anyone who is reasonably proficient in it will tell you. But it's not designed to be simple. It's designed to be useful.

>And you ask that to any newcommer to the language. Especially someone coming from
>languages which offer garbage collectors.
Most newcomers are either new to programming in general, in which case they should be learning about memory management to become well rounded programmers anyway, or new to C++ from a friendlier language like Java and were "protected" from the "evil" fundamentals of memory management. In the latter case, the result is a weak programmer who deserves the good kick in the pants that learning C++ will provide. I don't see the problem.

>"You don't need it because you have RAII" seems to me much like saying "You don't need
>new shoes because your torn old ones still have a sole"
How about "You don't need it because we don't need another feature added to an already feature rich language. Hey, here's how you can simulate that functionality with RAII". Which was the point made from the start, though you seem to have misunderstood.

VirtualAce
09-23-2006, 12:29 PM
This is why Java is at the very foundations is the antithesis of C++. Memory management in Java is non-existent and Java is not efficient by any means. It gets the job done, but if we were still stuck in the days of slower processors, Java would not even be an alternative. Java is useful for cross platform, but because it is so cross-platform it is about equally as inefficient on all platforms.

In my estimation, Java was aimed at everything and really ended up hitting nothing.

Some of the posts in this thread are the very reason I despise CLI as well. It adds language constructs to C++ to make it compatible with CLI, yet adds zero functionality to C++ outside of the CLI runtime environment. This is ridiculous.

For those of you that have been coddled and babied by this or that garbage collector it's time to learn how to take your own trash out. Proper memory management IMO is one of the most important skills a programmer can posess and I'm against any language, paradigm, construct, or what have you that attempts to make this process automatic and as far from the programmer as possible.

We run Java on many apps at work and I'm telling you it is a disaster in the works. Access to certain areas are slow, everytime it fires off another program in another window it has to load another Java runtime which just begins to slow the system down more and more. Amazingly when you get rid of the Java program and all its associated windows, the computer begins to act like a fast computer again. You should see the memory handles, allocations, and so forth skyrocket when the Java app is fired up. It is my estimation that the Java runtime usually does not de-allocate memory until the app is finished. This is a very very very bad practice. Whenever an application is done with memory, by all means give it back to the system then not later or whenever the hell you feel like it. Don't be a memory and resource hog and everyone other app on the system will be your friend. I can't believe we are talking about hogging memory today. Back in the day this would have been unheard of. But now since we have more memory than we know what to do with, everyone finds new ways to abuse it.
Just because you want to abuse the memory in your system and use it incorrectly either through a runtime or something else, does not mean everyone else does.

C++ is fine the way it is if it is used properly. No C++ is not a simple language nor does it coddle or baby you. You can mis manage the hell out of memory and C++ will just sit there and let you do it....until the inevitable crash comes. But what we have today is the problem that the 'crash' never happens which leads people to believe their code is fine when it's not. The crash never happens A. because we do have so much memory it takes forever to run out, and B. XP is always sitting there watching whats going on and even though I don't like XP a whole lot, it's memory management is much better than 98 and for the most part saves you from yourself. Every app finally does run inside the 'fence' and very rarely gets out of the fence. I've crashed XP but only using Direct3D. I don't think I've ever brought XP to its knees just doing Win32, MFC, or console code.

Adding constructs to the language that are just as error prone or moreso than simply using what has already been provided in the language is ludicrous.

Mario F.
09-23-2006, 12:54 PM
> For those of you that have been coddled and babied by this or that garbage collector it's time to learn how to take your own trash out.

No one suggested a garbage collector :rolleyes:

> Adding constructs to the language that are just as error prone or moreso than simply using what has already been provided in the language is ludicrous.

Sure it is. So lets ditch the boost libraries and do a joint protest against the proposed changes to C++0x. Just to name two. Aren't they as error prone as anything else? All it takes is to use them badly or not understand their purpose and meaning.

> C++ is fine the way it is if it is used properly.

That opinion is not shared by the standards committee.


How about "You don't need it because we don't need another feature added to an already feature rich language. Hey, here's how you can simulate that functionality with RAII". Which was the point made from the start, though you seem to have misunderstood.

Oh I understood that point. However you didn't seem to understand that my argument is exactly that is not a valid argument. :rolleyes:

It never was a good argument. The language never evolved that way. The language is still evolving and hopefully will always be be. And one of the main objectives behind the evolution of the language is to make it easier to use. Do I need to provide links to the objectives behind C++0x or even those behind C++98?

Do I think function destructors as proposed by Magos are a good idea? Personally I don't. Not the First-Last block no. I would rather much prefer a construct similar to what some compilers already offer (yes. Surprise. Some compilers do offer function destructors as one of their function attributes provided as an extension to the language). But even so I don't have a definite opinion. I simply don't have the technical knowledge to ascertain it's a good or bad idea.

But I don't see any merit in the "you already have RAII" argument either. It never stopped the standards committee to adopt new constructs despite the fact things could have stayed the same.

jlou
09-23-2006, 01:58 PM
Aren't they as error prone as anything else?No, they are not. And that is the entire reason why many of them are added to the standard.

These new libraries and features boost productivity and make code better, safer, easier to write and easier to maintain. That is why they are added.

It is up to proponents of the finally block or the function destructor to explain how they do that as well, especially given the existing alternatives.

Prelude
09-23-2006, 02:05 PM
>The language never evolved that way.
You don't know much about C++'s evolution then. When faced with the need for a new feature, the first thing done has always been to find a viable alternative using existing features. Then an attempt was made to simulate the feature with a library. Only if neither of those could be done and the need for a feature was great, did they actually consider adding to the language. And keep in mind that the acceptance of new features is exceedingly strict, even if a proposal does get that far.

In this case, RAII is a viable alternative, so the proposal (however informal) stops there.

>It never stopped the standards committee to adopt new constructs despite the fact things could have stayed the same.
Could you list some of them? Most likely you're misunderstanding either the constructs or the intentions of the committee. By the way, we're talking about actual language features, not library additions.

Mario F.
09-23-2006, 02:43 PM
Well, The most likely changes discussed in http://www.artima.com/cppsource/cpp0x.html show constructs that are without exception ways to improve and speed code despite there being alternatives. Since we are talking of actual languages features, of those discussed there I name using and auto. But could also possibly name the sequence constructor needed to support array-like initialization of sequence containers.

But I guess that you are going to give me reasons that in your opinion make these ok changes and not at all related to what I have been saying.

CornedBee
09-23-2006, 07:06 PM
In C (and C++, not using RAII (http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization)), the "goto cleanup" pattern is commonly used:
You do realize that this is utterly exception-unsafe, do you?


But really no one came up with a good argument yet as to why function destructors are not a good idea. In my limited knowledge I don't see anything absurd in this addition to the language.
It is not absurd, as such. Other languages have added it with some success. It's just that it provides no improvement over the current situation.


And if the best one can come up with is "you have RAII for that", then really most of the current language features would have to be removed since they too present coding alternatives. I just don't think that is a valid argument.
Then you misunderstand the way the argument is presented, or perhaps the argument is presented poorly.
In other words, I challenge you to show me one feature of the C++ core language that
1) is not required for C compatibility (such as the struct/class duality),
2) can be simulated with the remaining language features without
2a) significantly increasing source code size or
2b) making the feature inherently more unsafe to use.
The using statement (i.e. using namespace xxx) does not count - I actually think it's useless and tends to lead newbies to bad practices.

Under these criteria, try...finally falls short because it can, as I have shown, be simulated using local classes and their destructors without significant source or safety overhead.


In an effort to simplify the language, RAII is not the answer (albeit I'm not arguing against the merits of this paradigm). It only further complicates it.
On the contrary. Because RAII is not a language feature, it keeps the language simpler. Instead, RAII is implemented using existing language features, thus leveraging the user's existing expertise (sorry, just wanted to say that).


So lets ditch the boost libraries
The boost libraries are not a language feature. They're libraries - implemented on top of existing language features. Your argument makes no sense.


I would rather much prefer a construct similar to what some compilers already offer
Rename the sentinel in my post to function_destructor and you have the feature.


constructs that are without exception ways to improve and speed code despite there being alternatives.
Yes, that's the point: improve and speed code. try...finally does not do this.


I name using
The current alternative: nested typedefs. The example line would translate to this:

template <typename T> struct Vec { typedef std::vector<T, My_Alloc<T> > type; };

Vec<float>::type v;
The code might not be significantly longer, but it contains a readability impact every time you use it. And it is not equivalent: you cannot pass Vec as a template template parameter. (I'm not fully sure if you can do it with the using, but I assume you can.) Therefore, this new language feature provides something that is not possible without it.


and auto.
The use of auto is twofold; both uses have been presented in the "a few doubts" thread.
First, it can significantly reduce typing and simply detective work for complex types: an average Boost.Spirit rule has a full type that may be anywhere from 60 to 400 characters long. In other words, it is not practical to create a local variable of a Spirit rule type without type erasure in the form of spirit::rule. auto changes that.
The other use is something that is currently just about impossible to solve with existing features: local variables in templates that depend on a potentially non-traceable way on the template parameters, e.g. the result of an overloaded function called with a parameter of template parameter type. This simple case can be handled by boost::result_of (with significant compile-time overhead, I believe) - more complex cases require BOOST_TYPEOF, and this macro in turn requires libraries to register their types in order for it to work.


But could also possibly name the sequence constructor needed to support array-like initialization of sequence containers.
The sequence constructor makes static initialization of containers possible - this is not possible with the current language features.

As you see, none of these features are redundant: they all provide something that is currently not possible, or requires a significant of work to duplicate.

Prelude
09-23-2006, 07:24 PM
>But I guess that you are going to give me reasons that in your opinion make these ok
>changes and not at all related to what I have been saying.
Hmm, I have a better idea. I'll stop wasting my time and let you believe whatever you want. That's not the reaction anyone wants from me, but you've certainly earned it. :)

Mario F.
09-23-2006, 07:35 PM
Oh, I do agree with the fact they aren't redundant. However, It doesn't confuse me either the fact they can be seen as... fluff, considering there are already means to achieve the same results for the most cases. It's all in the usage pattern of the coder... and not everyone uses Spirit.

I see these changes as positive. I still shudder somewhat towards auto. But given your confidence on the a few doubts thread, I definitely have to accept that as my ignorance on the matter.


In other words, I challenge you to show me one feature of the C++ core language that
1) is not required for C compatibility (such as the struct/class duality),
2) can be simulated with the remaining language features without
2a) significantly increasing source code size or
2b) making the feature inherently more unsafe to use.
The using statement (i.e. using namespace xxx) does not count - I actually think it's useless and tends to lead newbies to bad practices.

I can't. You present me too many restrictions. I could possibly include auto there. But again I'm fearful of doing it. I clearly cannot understand it fully yet. You don't let me put the using directive, so that's out too. The only other thing I can think of is auto_ptr because it has no multiple instances protection. But then and again auto_ptr is really not a language feature. Most of what is "wrong" with C++ is of what derives from its needed compatibility with C. But I agree that is a mute argument.


Rename the sentinel in my post to function_destructor and you have the feature.

I can start to see the non merits of this feature. But I'm glad it evolved past the simple "RAII already does that".


The boost libraries are not a language feature. They're libraries - implemented on top of existing language features. Your argument makes no sense.

It was an absurd reply to an absurd statement.

Mario F.
09-23-2006, 07:42 PM
Hmm, I have a better idea. I'll stop wasting my time and let you believe whatever you want. That's not the reaction anyone wants from me, but you've certainly earned it. :)

Not really. I don't desire that kind of reaction from you. Especially from you, whose opinions I respect. However, I rather much prefer be taken seriously (like you obviously do too) and benefit from more than a play of words where I have to constantly be reafirming what is that I don't understand or agree, only to have as a reply more of the same.

When that happens, yes. I agree with you. It's best if we stop wasting time.

CornedBee
09-23-2006, 08:02 PM
and not everyone uses Spirit.
It was just an example. Expression templates are becoming more common all the time.


I can't. You present me too many restrictions.
Oh, but they're only two restrictions. The second one is merely a more exact formulation of "useful, but not redundant".


I could possibly include auto there.
No. As I have shown, it is not possible to fully emulate the functionality of auto with existing functionality.


It was an absurd reply to an absurd statement.
But the argument was exactly the same as in my challenge, just perhaps not as ... politically formulated ;)

I think, though, in this case Prelude is too focused on her understanding of the language and could argue better if she tried to look at the situation from the point of view of someone with less experience in the dark areas of in-depth C++ knowledge.

Prelude
09-24-2006, 06:26 AM
>I think, though, in this case Prelude is too focused on her understanding of the language and
>could argue better if she tried to look at the situation from the point of view of someone
>with less experience in the dark areas of in-depth C++ knowledge.
That's probably true. :(