Where is RAII when you need it?

S

Stefan Ram

Today, I wanted to show RAII, like this:

#include <iostream>
#include <ostream>
#include <stdexcept>

struct K
{ K (){ ::std::cerr << "K" << ::std::endl; }
~K(){ ::std::cerr << "~K" << ::std::endl; }};

struct R
{ K * k; R() : k{ new K{} }{}
~R() { ::std::cerr << "~R" << ::std::endl; delete k; }};

int main(){ R r{}; throw ::std::runtime_error( "error" ); }

The output was:

K
terminate called after throwing an instance of 'std::runtime_error'
what(): error

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
 
A

Alf P. Steinbach

Today, I wanted to show RAII, like this:

#include <iostream>
#include <ostream>
#include <stdexcept>

struct K
{ K (){ ::std::cerr << "K" << ::std::endl; }
~K(){ ::std::cerr << "~K" << ::std::endl; }};

struct R
{ K * k; R() : k{ new K{} }{}
~R() { ::std::cerr << "~R" << ::std::endl; delete k; }};

int main(){ R r{}; throw ::std::runtime_error( "error" ); }

The output was:

K
terminate called after throwing an instance of 'std::runtime_error'
what(): error

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

This is the usual process exit code from `abort`.

No ~K nor ~R in sight! Why?

It's unspecified whether stack unwinding happens when there is no
handler for an exception.

In this case, with this compiler, it didn't happen.

For my own little hobby programming I therefore (among many other
semi-good reasons) use a write-it-once-and-be-done-with-it `main`
function that catches any standard exception, as follows:


[code file="cppx/process/invoke_main.hpp"]
#pragma once
// Copyright (c) 2013 Alf P. Steinbach.

#include <rfc/cppx/Type_.h> // cppx::Type_, cppx::Type
#include <rfc/cppx/process/Exit_code.h> //
cppx::process::Exit_code::Enum

#include <functional> // std::function
#include <iostream> // std::clog
#include <stdexcept> // std::exception
#include <system_error> // std::system_error

namespace cppx{ namespace process{
using std::exception;
using std::function;

typedef Type_T_<void()>::T Main_function;
typedef function<Main_function> Main_functor;

typedef cppx::Type_T_<bool( exception const& )>::T Logger_function;
typedef function<Logger_function> Logger_functor;

inline bool stderr_logger( exception const& x )
{
using namespace std;
cerr << "!" << x.what();
if( auto p_syserr = dynamic_cast<system_error const*>( &x ) )
{
cerr << " (error code 0x" << uppercase << hex <<
p_syserr->code().value() << ")";
}
cerr << endl;
return !!clog;
}

inline int invoke_main(
Main_functor const& cpp_main,
Logger_functor const& log = stderr_logger
)
{
try
{
cpp_main();
return Exit_code::success;
}
catch( exception const& x )
{
log( x );
return Exit_code::failure;
}
catch( ... )
{
throw; // Uh oh.
}
}
} } // namespace cppx::process
[/code]


which you can use like this (also this provided in an include file, but
it's questionable whether that can be called a "header"):


[code file="cppx/default_main.impl.hpp"]
// Implementation file.

#include <rfc/cppx/process/invoke_main.hpp>

extern void cpp_main();

auto main() -> int { return cppx::process::invoke_main( &cpp_main ); }
[/code]


whence we get down to the nitty-gritty, namely what you then write in
each using program, like this:


void cpp_main()
{
// blah blah, my "main program" code, may throw
}

#include <rfc/cppx/default_main.impl.hpp>


Of course the include directive can be anywhere, and alternatively it
can be replaced with linking, or even (gasp!) writing the one-liner
`main` -- shown earlier above -- oneself...


Cheers & hth.,

- Alf

PS: There has been an article series about using a standard
write-it-once-and-be-done-with-it `main` in the ACCU Overload Journal. I
haven't read it though. Skimming lightly through one of the articles I
think I saw some not very general interest code there, but it may still
be worth checking out.
 
S

Stefan Ram

Richard Damon said:
I believe that the uncaught throw can terminate before doing the stack
unwinding. If you moved your code from main into a function that main
calls, and then have main catch the thrown exception, then you would get
what you were expecting.

I already have observed this before I posted my program.
However, I thought that RAII was exception-safe even without
the need for try-catch blocks. I wonder whether the observed
behavior is a problem of some C++ implementations or allowed
or required by the standard.
 
M

Melzzzzz

I believe that the uncaught throw can terminate before doing the
stack unwinding. If you moved your code from main into a function
that main calls, and then have main catch the thrown exception, then
you would get what you were expecting.

I already have observed this before I posted my program.
However, I thought that RAII was exception-safe even without
the need for try-catch blocks. I wonder whether the observed
behavior is a problem of some C++ implementations or allowed
or required by the standard.
[/QUOTE]

c++ always simplifies implementations. exception handlers(internal) are
usually functions that execute destructors, but since in this case
there are no handlers, abort() is called directly.
For destructors to be called implementation should install additional
handler(even you didn;t want to) which is bit hackish.
 
A

Alf P. Steinbach

I already have observed this before I posted my program.
However, I thought that RAII was exception-safe even without
the need for try-catch blocks. I wonder whether the observed
behavior is a problem of some C++ implementations or allowed
or required by the standard.

The latter.

Cheers & hth.,

- Alf
 
I

Ian Collins

Alf said:
For my own little hobby programming I therefore (among many other
semi-good reasons) use a write-it-once-and-be-done-with-it `main`
function that catches any standard exception, as follows:
which you can use like this (also this provided in an include file, but
it's questionable whether that can be called a "header"):


[code file="cppx/default_main.impl.hpp"]
// Implementation file.

#include <rfc/cppx/process/invoke_main.hpp>

extern void cpp_main();

auto main() -> int { return cppx::process::invoke_main( &cpp_main ); }
[/code]


whence we get down to the nitty-gritty, namely what you then write in
each using program, like this:


void cpp_main()
{
// blah blah, my "main program" code, may throw
}

#include <rfc/cppx/default_main.impl.hpp>


Of course the include directive can be anywhere, and alternatively it
can be replaced with linking, or even (gasp!) writing the one-liner
`main` -- shown earlier above -- oneself...

That's an awfully complicated way to implement a wrapper for main.

Just about all of my applications use a code generated main that parses
the application's command line options into a struct which it passes to
the application entry point. I think this is both as simple and as
complex as the solution needs to be.

An example (io is the application namespace, utils::ArgV is a typedef
for a vector of strings):

void run( const io::Setup&, utils::ArgV& );

int
main( int argc, char** argv )
{
try
{
io::setup.argv0 = argv[0];

utils::loadOptions();

utils::ArgV args( &argv[1], &argv[argc] );

io::Option::load( args, io::setup, io::requiredOptions );

run( io::setup, args );

return EXIT_SUCCESS;
}
catch( const std::exception& e )
{
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
}
 
A

alf.p.steinbach

Alf said:
For my own little hobby programming I therefore (among many other
semi-good reasons) use a write-it-once-and-be-done-with-it `main`
function that catches any standard exception, as follows:
which you can use like this (also this provided in an include file, but
it's questionable whether that can be called a "header"):


[code file="cppx/default_main.impl.hpp"]
// Implementation file.

#include <rfc/cppx/process/invoke_main.hpp>

extern void cpp_main();

auto main() -> int { return cppx::process::invoke_main( &cpp_main ); }
[/code]


whence we get down to the nitty-gritty, namely what you then write in
each using program, like this:


void cpp_main()
{
// blah blah, my "main program" code, may throw
}

#include <rfc/cppx/default_main.impl.hpp>


Of course the include directive can be anywhere, and alternatively it
can be replaced with linking, or even (gasp!) writing the one-liner
`main` -- shown earlier above -- oneself...

That's an awfully complicated way to implement a wrapper for main.

It has different responsibilities than yours.

Alf's wrapper:

* Fixed code.
* System independent (in particular should work in Windows).
* Exception handler.
* Customizable exception logger.
* Customizable choice of main function to call.

Ian's wrapper:

* Generated.
* Exception handler.
* Argument parsing.

Since the argument parsing in the code you posted uses the `main` function arguments, it's limited to *nix systems (UTF-8) and possibly the national byte-oriented character set in Windows (Windows ANSI). That doesn't matter if all you ever do is ASCII options. But passing filenames = problem.

In Windows you can get the UTF-16 command line from `GetCommandLine`, and astandard but not quite perfect parsing via `CommandLineToArgvW`.

Just about all of my applications use a code generated main that parses
the application's command line options into a struct which it passes to
the application entry point. I think this is both as simple and as
complex as the solution needs to be.

An example (io is the application namespace, utils::ArgV is a typedef
for a vector of strings):

void run( const io::Setup&, utils::ArgV& );

int
main( int argc, char** argv )
{
try
{
io::setup.argv0 = argv[0];

utils::loadOptions();

utils::ArgV args( &argv[1], &argv[argc] );

io::Option::load( args, io::setup, io::requiredOptions );

run( io::setup, args );

return EXIT_SUCCESS;
}
catch( const std::exception& e )
{
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
}


I guess this code or the code generator was written before C++11. Just a suggestion, of course, but the code I posted also picks up the error code from a std::system_exception. Sometimes it's very nice to know the error code,whether it's a Posix one or a Windows one (unfortunately, IMHO, the C++11 standard treats Posix error codes as kind of general).


Cheers, & thanks,

- Alf
 
I

Ian Collins

(e-mail address removed) wrote:

Please fix your google horribleness!
It has different responsibilities than yours.

Fair enough, but do you really need things like Exit_code::success when
the standard already provides success and failure values?
Alf's wrapper:

* Fixed code.
* System independent (in particular should work in Windows).

Run away!
* Exception handler.
* Customizable exception logger.

I can specify a logger in the options file.
* Customizable choice of main function to call.

I can't see the point of this.
Ian's wrapper:

* Generated.
* Exception handler.
* Argument parsing.

Since the argument parsing in the code you posted uses the `main`
function arguments, it's limited to *nix systems (UTF-8) and possibly
the national byte-oriented character set in Windows (Windows ANSI). That
doesn't matter if all you ever do is ASCII options. But passing
filenames = problem.

If the options get too hairy, the interface supports a JSON file or
command line JSON blob.
 
A

alf.p.steinbach

(e-mail address removed) wrote:

Please fix your google horribleness!

Oh my, Google Groups is posting with horrible Quoted Printable!

Ouch!

It's bad enough having to delete every second quoted line (which are just inserted blanks), and knowing that I can't use a standard signature delimiter (it will get messed up), and that the reference lists are arbitrarily broken, etc. I'm accepting all that because for some reason EternalSeptember now refuses to post articles (I can read 'em via NNTP, but not post). I see you're using individual.net, and I used to use them many years ago: a really fine, good, nice initiative, AND they've always run CleanFeed. But -- when they started charging a small fee, I was unable to find a way to pay thatworked. :-(

Fair enough, but do you really need things like Exit_code::success when
the standard already provides success and failure values?

Not really, but it's for consistency with Exit_code::failure, which /is/ needed -- due to a low-quality convention for EXIT_FAILURE.

In general Exit_code::failure is defined as EXIT_FAILURE, which is usually defined as 1, but for Windows Exit_code::failure defined as

auto const exitcode_failure = 0x80004005L; // E_FAIL

because the Windows API defines the value 1 as


// MessageId: ERROR_INVALID_FUNCTION
//
// MessageText:
//
// Incorrect function.
//
#define ERROR_INVALID_FUNCTION 1L // dderror


and also, later in the same file (namely [winerror.h]) it defines


#define S_FALSE ((HRESULT)1L)


just to be ambiguous I guess.

The whole idea of tens of thousands /success codes/ boggles my mind. I can't fathom what they were thinking. But, there we are: it's how it is.

Anyway, when the value 1 signifies an error, then in Windows it is ERROR_INVALID_FUNCTION, and so with the common Windows convention that EXIT_FAILURE= 1 one simply does not want to use EXIT_FAILURE: one could not then distinguish general failure from ERROR_INVALID_FUNCTION.

Run away!

He he. :)

I can specify a logger in the options file.


I can't see the point of this.

That's just to simplify the usage when a main function is supplied by some 3rd part library. It doesn't cost anything. Well not much. ;-)


[snip]
If the options get too hairy, the interface supports a JSON file or
command line JSON blob.


Oh, good idea.


Cheers,

- Alf
 
I

Ian Collins

Oh my, Google Groups is posting with horrible Quoted Printable!

And silly long lines...
Ouch!

I see you're using individual.net, and I used to use them many years
ago: a really fine, good, nice initiative, AND they've always run
CleanFeed. But -- when they started charging a small fee, I was unable
to find a way to pay that worked. :-(

They've improved the payment options.
Not really, but it's for consistency with Exit_code::failure, which
/is/ needed -- due to a low-quality convention for EXIT_FAILURE.

In general Exit_code::failure is defined as EXIT_FAILURE, which is
usually defined as 1, but for Windows Exit_code::failure defined as

auto const exitcode_failure = 0x80004005L; // E_FAIL

because the Windows API defines the value 1 as

So windows doesn't comply with the standard?
// MessageId: ERROR_INVALID_FUNCTION
//
// MessageText:
//
// Incorrect function.
//
#define ERROR_INVALID_FUNCTION 1L // dderror

Ah, I see. But those codes are different from the programme exit codes,
aren't they? If you want to be cross-platform, you should use EXIT_FAILURE.
 
A

Alf P. Steinbach

So windows doesn't comply with the standard?


Ah, I see. But those codes are different from the programme exit codes,
aren't they?

Not inherently or necessarily, no. It depends of course on what one
chooses to use process exit codes for. One can just convey 1 bit of
information (failure versus success) or one can convey the full 8 bits
available in *nix or 32 bits (minus one special value) available in
Windows, which is still compatible with the 1-bit view wrt. scripts etc.

Having the original failure cause available at process exit is most
often irrelevant but sometimes really handy. ;-)

If you want to be cross-platform, you should use EXIT_FAILURE.

That's the general platform-independent choice, yes, as I tried to
explain above.

However, portability can't be affected by what one does especially for a
given known platform, as long as what one does there doesn't affect the
code for other platforms.

So, when the platform is X, and one knows the process exit code (if any
such exists) convention on X, then it would be silly to limit oneself to
the three values and 1 bit of information of the C++ standard.


Cheers,

- Alf
 
I

Ian Collins

Alf said:
That's the general platform-independent choice, yes, as I tried to
explain above.

However, portability can't be affected by what one does especially for a
given known platform, as long as what one does there doesn't affect the
code for other platforms.

So, when the platform is X, and one knows the process exit code (if any
such exists) convention on X, then it would be silly to limit oneself to
the three values and 1 bit of information of the C++ standard.

But you do have to make sure your exit value is < INT_MAX, and
0x80004005L isn't!
 
S

Stefan Ram

Alf P. Steinbach said:
That's the general platform-independent choice, yes, as I tried to
explain above.

Microsoft exit codes like E_FAIL were, IIRC, never intended
to be used as process exit codes. They are returned from
certain functions, IIRC, especially from methods of COM.
 
A

Alf P. Steinbach

But you do have to make sure your exit value is <= INT_MAX, and
0x80004005L isn't!

If the goal was to have portability that only relied on the C++
standard's guarantees, yes, then that would be true.

However, for this platform-specific code we're in platform-specific land.

It's no big deal to do the standard-conforming thing /if/ one feels that
it's necessary, e.g. `-static_cast<int>(1 + ~0x80004005uL)`, which in
Windows (which has 32-bit `int`, even in 64-bit Windows, with negation
as two's complement) produces the same bitpattern as an `int`.

But many such small inconveniences add up, so no, I do not choose to do
that.

It's the same as one does not add work to support theoretical compilers
that, as permitted by the C++ standard, use 10 MB per `bool` variable...

I.e. what the standard permits, by way of not pinning down or by way of
totally permissive UB, and what one needs to consider in practice where
market forces are at play, are two very different things. ;-)

And as I see it, the C++ standard is designed for those who have such a
pragmatic view of things -- /practical/ programming.

0x80004005L as a direct expression has the very practical benefits of
not only being simple and clear, but also being easily searchable and
recognizable in the Windows API headers, i.e. easily provable correct.

I leave it to those who feel strongly enough about it, those who really
really Really want to use an impractical tool or tool setting such as
g++ -ftrapv, to go through the contortions exemplified above in order to
achieve the same result with added verbosity and less clear code.


Cheers,

- Alf (in just-my-2-cents mode)
 
A

Alf P. Steinbach

Alf P. Steinbach said:
That's the general platform-independent choice, yes, as I tried to
explain above.

Microsoft [error] codes like E_FAIL were, IIRC, never intended
to be used as process exit codes.

Right.

They are however eminently usable as process exit codes.

The only process exit code to avoid in Windows is STILL_ACTIVE, value
259, which indicates to any caller of GetExitCodeProcess that the
process is still running... This is also the ERROR_NO_MORE_ITEMS value.
Both the error code and the situation of some code polling the process
exit code in order to wait on a process, are very rare, but it might
still be worth replacing ERROR_NO_MORE_ITEMS with E_FAIL -- to
intentionally ditch fault information in order to ensure correctness.

They are returned from
certain functions, IIRC, especially from methods of COM.

Yes, there's a large system of error codes, of various conceptual types
(the two main types are plain error codes where x!=0 is error, and
HRESULT error codes where int(x)<0 is error, but there's also SCODE used
in native exceptions, and some more local schemes: a jungle!).

The C++11 std::system_error & related functionality map them all, in an
implementation-defined way, to Posix error codes, for general treatment.

As I understand it the Posix codes are limited to 8 bits unsigned value
range. And so, used as process exit codes they avoid the STILL_ACTIVE
problem in Windows. However, that would lose some detailed information
and would preclude using convenient tools such as Microsoft's `errlook`.


Cheers & hth.,

- Alf (who has several volumes worth more to say about this topic, but
the discussion is already on the edge of off-topicality, as I see it)
 
G

Geoff

So windows doesn't comply with the standard?

Windows complies with the standard. Alf is trolling.

From stdlib.h:
/* Definition of the argument values for the exit() function */

#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1

These are the codes in WinError.h and are the Win32 API error return
codes, not application error return codes and they aren't intended to
be used as such.
Ah, I see. But those codes are different from the programme exit codes,
aren't they? If you want to be cross-platform, you should use EXIT_FAILURE.

Correct. The correct method of error exit from a Windows program is to
call ExitProcess:

ExitProcess(EXIT_FAILURE);
 
A

Alf P. Steinbach

Windows complies with the standard.

That's meaningless, sorry.

I think Ian's question was rhetorical, where he assumed that I and any
(reasonable) readers would see the question as meaningless. The idea
being that the connection between the two areas was not apparent at that
time. Just below it then got more apparent, with an "I see".

Not sure why I bother to explain that to you, though, when you go on to say

Alf is trolling.

That's a non-technical, off-topic, out of the blue personal attack, and
together with the lack of any meaningful correct technical content,
earned you a plink.

"plink" means that I will not be reading your future articles, and that
others may treat you likewise.

From stdlib.h:
/* Definition of the argument values for the exit() function */

#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1


These are the codes in WinError.h and are the Win32 API error return
codes, not application error return codes

That's right, and nobody has so far been in any doubt about that.

and they aren't intended to be used as such.

That's a meaningless assertion, sorry.


Nope, it's wrong in the original context.

Although it's true out of context.

The correct method of error exit from a Windows program is to
call ExitProcess:

ExitProcess(EXIT_FAILURE);

That's absolutely not smart in ordinary application level C++.

It does not involve the runtime library, and so open files will not be
properly closed (although their handles will be freed), the stack will
not be unwinded, exit handlers will not be called, no
application-specific cleanup whatsoever.

The runtime library will however use that method, after it's done
cleaning up.


- Alf (plinking you: no correct technical content, ad hominem attack)
 
G

Geoff

That's meaningless, sorry.

Not meaningless. Microsoft defines EXIT_SUCCESS and EXIT_FAILURE as 0
and 1 per the standard. The fact that other macros are defined as 0
and one is meaningless and irrelevant.
I think Ian's question was rhetorical, where he assumed that I and any
(reasonable) readers would see the question as meaningless. The idea
being that the connection between the two areas was not apparent at that
time. Just below it then got more apparent, with an "I see".

I respect Ian enough to know a rhetorical question when I see it and
to know that if he took the time to write it that it is not
meaningless.
Not sure why I bother to explain that to you, though, when you go on to say



That's a non-technical, off-topic, out of the blue personal attack, and
together with the lack of any meaningful correct technical content,
earned you a plink.

Interesting that you consider a statement of fact to be a personal
attack since your exposition on the plethora of error codes was both
irrelevant and meaningless in the original context.
"plink" means that I will not be reading your future articles, and that
others may treat you likewise.



That's right, and nobody has so far been in any doubt about that.

But you raised the other Windows internal error values as though
EXIT_FAILURE didn't exist and seemed to be of the opinion that they
should be used routinely as application return codes. This is false.
That's a meaningless assertion, sorry.

I see. But your assertion that they are valid application returns even
though they are non-portable and non-stander is meaningful?
Nope, it's wrong in the original context.

Although it's true out of context.



That's absolutely not smart in ordinary application level C++.

It does not involve the runtime library, and so open files will not be
properly closed (although their handles will be freed), the stack will
not be unwinded, exit handlers will not be called, no
application-specific cleanup whatsoever.

The handles are not freed, they are closed. There is a significant
difference. The word is unwound.
The runtime library will however use that method, after it's done
cleaning up.


- Alf (plinking you: no correct technical content, ad hominem attack)

Thank you. I would wear my Steinbach plink as a badge of honor but you
seem to be rather promiscuous and indiscriminate with them.
 
A

Alf P. Steinbach

But you do have to make sure your exit value is < INT_MAX, and
0x80004005L isn't!

Addendum: I changed the enum that client code uses, to be based on `int`.


Code:
struct Exit_code
{
enum Enum: int
{
success = EXIT_SUCCESS,
failure = static_cast<int>(
cppx::process::detail::exitcode_failure )
};
};


That moves the formal problem to compile time, where presumably g++'s
-ftrapv option can't reach. g++ can still refuse to compile the code
though, since I intentionally used just a relatively clear static_cast
rather than the more subtle and complicated bitlevel rewrite I showed
earlier. In such cases, is there any advantage in catering to possible
but very unlikely perverse compiler behavior?


Cheers,

- Alf
 
I

Ian Collins

Alf said:
But you do have to make sure your exit value is < INT_MAX, and
0x80004005L isn't!

Addendum: I changed the enum that client code uses, to be based on `int`.


Code:
struct Exit_code
{
enum Enum: int
{
success = EXIT_SUCCESS,
failure = static_cast<int>(
cppx::process::detail::exitcode_failure )
};
};


That moves the formal problem to compile time, where presumably g++'s
-ftrapv option can't reach. g++ can still refuse to compile the code
though, since I intentionally used just a relatively clear static_cast
rather than the more subtle and complicated bitlevel rewrite I showed
earlier. In such cases, is there any advantage in catering to possible
but very unlikely perverse compiler behavior?

It still looks like a pointless exercise. If you are only declaring one
failure value, why use something other than EXIT_FAILURE?

What value does this apparent over complication add?
 

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

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top