Solved: I figured out a clean way to do it with setjmp()/longjmp(), requiring only a minimal wrapper like:
int jump(jmp_buf j, int i) { longjmp(j, i); return 0; }
This allows jump() to be used in conditional expressions. So now the code:
if (A == 0) return;
output << "Nonzero.\n";
Is correctly translated to:
return
((A == 0) && jump(caller, 1)),
(output << "Nonzero.\n"),
0;
Where caller is a jmp_buf back to the point of invocation in the calling function. Clean, simple, and efficient to a degree that is far less implementation-defined than exceptions. Thank you for your help!
Is there a way to emulate the use of flow-control constructs in the middle of an expression? Is it possible, in a comma-delimited expression x, y, for y to cause a return?
Edit: I'm working on a compiler for something rather similar to a functional language, and the target language is C++. Everything is an expression in the source language, and the sanest, simplest translation to the destination language leaves as many things expressions as possible. Basically, semicolons in the target language become C++ commas. In-language flow-control constructs have presented no problems thus far; it's only return. I just need a way to prematurely exit a comma-delimited expression, and I'd prefer not to use exceptions unless someone can show me that they don't have excessive overhead in this situation.
The problem of course is that most flow-control constructs are not legal expressions in C++. The only solution I've found so far is something like this:
try {
return
x(), // x();
(1 ? throw Return(0) : 0); // return 0;
} catch (Return& ret) {
return ref.value;
}
The return statement is always there (in the event that a Return construct is not reached), and as such the throw has to be wrapped in ?: to get the compiler to shut up about its void result being used in an expression.
I would really like to avoid using exceptions for flow control, unless in this case it can be shown that no particular overhead is incurred; does throwing an exception cause unwinding or anything here? This code needs to run with reasonable efficiency. I just need a function-level equivalent of exit().
You may want to research cfront, which is a program from the late 80's/early 90's that translated C++ into C (no templates or exceptions back then), because there were few, if any, native C++ compilers.
The way it handled inline functions is very similar to what you are trying to do: lots of trinary (?:) operators, commas, and parentheses. However, it could not convert an inline function with control flow more complex than if/then, e.g. a for or while loop, to an expression, and would have to implement that function as non-inline.
The only way to "prematurely exit a comma-delimited expression" would be with the trinary operator and parentheses. For example:
(
first thing,
second thing,
test expression?
(
next thing if successful,
another thing,
return value
)
:( // How often can you use an emoticon as an operator, anyway?
something to do if unsuccessful,
more cleanup,
return value
)
)
If the compiler doesn't short-circuit the then and else clauses of the trinary operator, you're out of luck.
what for? C++ is imperative language. Expressions there are just expressions. Use functional languages if you want to do everything as expressions/functions.
I get the feeling you just have a functional specification (in terms of pre- and post-conditions, for example) of how the translation process must be performed. Since C++ is not a declarative language, but an imperative one, you have to derive yourself a procedural implementation of that translation process before you start coding. And, as you have already seen, it's not as simple as concatenating all your original expressions using commas.
What you are trying to do is have the C++ compiler do your work for you. This won't work, since C++ is not a declarative language, and its compiler won't dynamically try to interpret what you meant from your specifications. And, if this could work, C++ would have to be just another dynamic declarative language, and you would be probably targeting another static language.
A hint on what could work: Analyze every original expression completely (with its possible side-effects) and only then output code. If your expression is compound (it has sub-expressions), don't output anything until you have analyzed the bigger expression.
Related
Still getting used to the formatting when writing C++ code, come from a Lua background.
How do I correctly format a if/conditional expressions as my example highlights below.
This will correctly run, but with warnings which is unideal:
return (boolean == true) and init() or 0;
Highlighted Warning:
expected a ';'C/C++ (between "(boolean == true)" "and")
and and or are keywords are considered a bit archaic. They are technically valid C++ keywords in modern C++ (since C++98 it seems). You must be using a very old C++ compiler that was written before they were added to C++. They, and their usage, never took off. Classical && and || operators continue to rule the roost and are not going anywhere.
Therefore: although it is true that this expression should be logically equivalent to boolean && init(), you might want to consider assigning somewhat higher priority of updating your C++ compiler to something more modern, if that's indeed the reason for the compilation error.
We are writing safety-critical code and I'd like a stronger way than [[nodiscard]] to ensure that checking of function return values is caught by the compiler.
[Update]
Thanks for all the discussion in the comments. Let me clarify that this question may seem contrived, or not "typical use case", or not how someone else would do it. Please take this as an academic exercise if that makes it easier to ignore "well why don't you just do it this way?". The question is exactly whether it's possible to create a type(s) that fails compiling if it is not assigned to an l-value as the return result of a function call .
I know about [[nodiscard]], warnings-as-errors, and exceptions, and this question asks if it's possible to achieve something similar, that is a compile time error, not something caught at run-time. I'm beginning to suspect it's not possible, and so any explanation why is very much appreciated.
Constraints:
MSVC++ 2019
Something that doesn't rely on warnings
Warnings-as-Errors also doesn't work
It's not feasible to constantly run static analysis
Macros are OK
Not a runtime check, but caught by the compiler
Not exception-based
I've been trying to think how to create a type(s) that, if it's not assigned to a variable from a function return, the compiler flags an error.
Example:
struct MustCheck
{
bool success;
...???...
};
MustCheck DoSomething( args )
{
...
return MustCheck{true};
}
int main(void) {
MustCheck res = DoSomething(blah);
if( !res.success ) { exit(-1); }
DoSomething( bloop ); // <------- compiler error
}
If such a thing is provably impossible through the type system, I'll also accept that answer ;)
(EDIT) Note 1: I have been thinking about your problem and reached the conclusion that the question is ill posed. It is not clear what you are looking for because of a small detail: what counts as checking? How the checkings compose and how far from the point of calling?
For example, does this count as checking? note that composition of boolean values (results) and/or other runtime variable matters.
bool b = true; // for example
auto res1 = DoSomething1(blah);
auto res2 = DoSomething2(blah);
if((res1 and res2) or b){...handle error...};
The composition with other runtime variables makes it impossible to make any guarantee at compile-time and for composition with other "results" you will have to exclude certain logical operators, like OR or XOR.
(EDIT) Note 2: I should have asked before but 1) if the handling is supposed to always abort: why not abort from the DoSomething function directly? 2) if handling does a specific action on failure, then pass it as a lambda to DoSomething (after all you are controlling what it returns, and what it takese). 3) composition of failures or propagation is the only not trivial case, and it is not well defined in your question.
Below is the original answer.
This doesn't fulfill all the (edited) requirements you have (I think they are excessive) but I think this is the only path forward really.
Below my comments.
As you hinted, for doing this at runtime there are recipes online about "exploding" types (they assert/abort on destruction if they where not checked, tracked by an internal flag).
Note that this doesn't use exceptions (but it is runtime and it is not that bad if you test the code often, it is after all a logical error).
For compile-time, it is more tricky, returning (for example a bool) with [[nodiscard]] is not enough because there are ways of no discarding without checking for example assigning to a (bool) variable.
I think the next layer is to active -Wunused-variable -Wunused-expression -Wunused-parameter (and treat it like an error -Werror=...).
Then it is much harder to not check the bool because comparison is pretty much to only operation you can really do with a bool.
(You can assign to another bool but then you will have to use that variable).
I guess that's quite enough.
There are still Machiavelian ways to mark a variable as used.
For that you can invent a bool-like type (class) that is 1) [[nodiscard]] itself (classes can be marked nodiscard), 2) the only supported operation is ==(bool) or !=(bool) (maybe not even copyable) and return that from your function. (as a bonus you don't need to mark your function as [[nodiscard]] because it is automatic.)
I guess it is impossible to avoid something like (void)b; but that in itself becomes a flag.
Even if you cannot avoid the absence of checking, you can force patterns that will immediately raise eyebrows at least.
You can even combine the runtime and compile time strategy.
(Make CheckedBool exploding.)
This will cover so many cases that you have to be happy at this point.
If compiler flags don’t protect you, you will have still a backup that can be detected in unit tests (regardless of taking the error path!).
(And don’t tell me now that you don’t unit test critical code.)
What you want is a special case of substructural types. Rust is famous for implementing a special case called "affine" types, where you can "use" something "at most once". Here, you instead want "relevant" types, where you have to use something at least once.
C++ has no official built-in support for such things. Maybe we can fake it? I thought not. In the "appendix" to this answer I include my original logic for why I thought so. Meanwhile, here's how to do it.
(Note: I have not tested any of this; I have not written any C++ in years; use at your own risk.)
First, we create a protected destructor in MustCheck. Thus, if we simply ignore the return value, we will get an error. But how do we avoid getting an error if we don't ignore the return value? Something like this.
(This looks scary: don't worry, we wrap most of it in a macro.)
int main(){
struct Temp123 : MustCheck {
void f() {
MustCheck* mc = this;
*mc = DoSomething();
}
} res;
res.f();
if(!res.success) print "oops";
}
Okay, that looks horrible, but after defining a suitable macro, we get:
int main(){
CAPTURE_RESULT(res, DoSomething());
if(!res.success) print "oops";
}
I leave the macro as an exercise to the reader, but it should be doable. You should probably use __LINE__ or something to generate the name Temp123, but it shouldn't be too hard.
Disclaimer
Note that this is all sorts of hacky and terrible, and you likely don't want to actually use this. Using [[nodiscard]] has the advantage of allowing you to use natural return types, instead of this MustCheck thing. That means that you can create a function, and then one year later add nodiscard, and you only have to fix the callers that did the wrong thing. If you migrate to MustCheck, you have to migrate all the callers, even those that did the right thing.
Another problem with this approach is that it is unreadable without macros, but IDEs can't follow macros very well. If you really care about avoiding bugs then it really helps if your IDE and other static analyzers understand your code as well as possible.
As mentioned in the comments you can use [[nodiscard]] as per:
https://learn.microsoft.com/en-us/cpp/cpp/attributes?view=msvc-160
And modify to use this warning as compile error:
https://learn.microsoft.com/en-us/cpp/preprocessor/warning?view=msvc-160
That should cover your use case.
|| is less verbose and easier to type than if (...), so I'd like a way to do the following (which happens a lot in my code):
int bar() {...; return successFlag;}
int foo() {
bar() || return(...); // return early on errors
...
}
Is it possible?
Or even better, is it possible to return back to main from bar, instead of chaining through foo?
For the first part, no: return is a keyword, not an expression, so it cannot combined in that form. I think you are instinctively reading || literally as meaning "or" in the English language sense - obviously that's not what it means.
For the second part, you could - in theory - by using exceptions, but it is a strongly discouraged practice. Exceptions are intended for "exceptional circumstances", more specifically, error conditions from which the code cannot recover locally (in the context of the current function/method) and where the proper action has to be determined at a higher level.
Use of exceptions as a general flow control mechanism has two problems:
Performance: C++ compilers are optimized for "non-exceptional" code paths (because that's what should be happening normally) and so exception handling mechanisms tend to be very slow. This is not from lack of code optimization, but from the need of the mechanism to have a zero or near-zero cost if there are no exceptions, even if the cost is for exceptions to take a lot of work to process.
code clarity/testability: using exceptions for control flow makes code immensely hard to read and unit-test properly in isolation.
For the first part - NO. For the second: setjmp and longjmp. But take note that both of them subvert the usual C++ stack based destructor calls and probably don't mix well with exceptions.
See, what I don't get is, why should programs like the following be legal?
int main()
{
static const int i = 0;
i < i > i;
}
I mean, surely, nobody actually has any current programs that have expressions with no side effects in them, since that would be very pointless, and it would make parsing & compiling the language much easier. So why not just disallow them? What benefit does the language actually gain from allowing this kind of syntax?
Another example being like this:
int main() {
static const int i = 0;
int x = (i);
}
What is the actual benefit of such statements?
And things like the most vexing parse. Does anybody, ever, declare functions in the middle of other functions? I mean, we got rid of things like implicit function declaration, and things like that. Why not just get rid of them for C++0x?
Probably because banning then would make the specification more complex, which would make compilers more complex.
it would make parsing & compiling the
language much easier
I don't see how. Why is it easier to parse and compile i < i > i if you're required to issue a diagnostic, than it is to parse it if you're allowed to do anything you damn well please provided that the emitted code has no side-effects?
The Java compiler forbids unreachable code (as opposed to code with no effect), which is a mixed blessing for the programmer, and requires a little bit of extra work from the compiler than what a C++ compiler is actually required to do (basic block dependency analysis). Should C++ forbid unreachable code? Probably not. Even though C++ compilers certainly do enough optimization to identify unreachable basic blocks, in some cases they may do too much. Should if (foo) { ...} be an illegal unreachable block if foo is a false compile-time constant? What if it's not a compile-time constant, but the optimizer has figured out how to calculate the value, should it be legal and the compiler has to realise that the reason it's removing it is implementation-specific, so as not to give an error? More special cases.
nobody actually has any current
programs that have expressions with no
side effects in them
Loads. For example, if NDEBUG is true, then assert expands to a void expression with no effect. So that's yet more special cases needed in the compiler to permit some useless expressions, but not others.
The rationale, I believe, is that if it expanded to nothing then (a) compilers would end up throwing warnings for things like if (foo) assert(bar);, and (b) code like this would be legal in release but not in debug, which is just confusing:
assert(foo) // oops, forgot the semi-colon
foo.bar();
things like the most vexing parse
That's why it's called "vexing". It's a backward-compatibility issue really. If C++ now changed the meaning of those vexing parses, the meaning of existing code would change. Not much existing code, as you point out, but the C++ committee takes a fairly strong line on backward compatibility. If you want a language that changes every five minutes, use Perl ;-)
Anyway, it's too late now. Even if we had some great insight that the C++0x committee had missed, why some feature should be removed or incompatibly changed, they aren't going to break anything in the FCD unless the FCD is definitively in error.
Note that for all of your suggestions, any compiler could issue a warning for them (actually, I don't understand what your problem is with the second example, but certainly for useless expressions and for vexing parses in function bodies). If you're right that nobody does it deliberately, the warnings would cause no harm. If you're wrong that nobody does it deliberately, your stated case for removing them is incorrect. Warnings in popular compilers could pave the way for removing a feature, especially since the standard is authored largely by compiler-writers. The fact that we don't always get warnings for these things suggests to me that there's more to it than you think.
It's convenient sometimes to put useless statements into a program and compile it just to make sure they're legal - e.g. that the types involve can be resolved/matched etc.
Especially in generated code (macros as well as more elaborate external mechanisms, templates where Policies or types may introduce meaningless expansions in some no-op cases), having less special uncompilable cases to avoid keeps things simpler
There may be some temporarily commented code that removes the meaningful usage of a variable, but it could be a pain to have to similarly identify and comment all the variables that aren't used elsewhere.
While in your examples you show the variables being "int" immediately above the pointless usage, in practice the types may be much more complicated (e.g. operator<()) and whether the operations have side effects may even be unknown to the compiler (e.g. out-of-line functions), so any benefit's limited to simpler cases.
C++ needs a good reason to break backwards (and retained C) compatibility.
Why should doing nothing be treated as a special case? Furthermore, whilst the above cases are easy to spot, one could imagine far more complicated programs where it's not so easy to identify that there are no side effects.
As an iteration of the C++ standard, C++0x have to be backward compatible. Nobody can assert that the statements you wrote does not exist in some piece of critical software written/owned by, say, NASA or DoD.
Anyway regarding your very first example, the parser cannot assert that i is a static constant expression, and that i < i > i is a useless expression -- e.g. if i is a templated type, i < i > i is an "invalid variable declaration", not a "useless computation", and still not a parse error.
Maybe the operator was overloaded to have side effects like cout<<i; This is the reason why they cannot be removed now. On the other hand C# forbids non-assignment or method calls expresions to be used as statements and I believe this is a good thing as it makes the code more clear and semantically correct. However C# had the opportunity to forbid this from the very beginning which C++ does not.
Expressions with no side effects can turn up more often than you think in templated and macro code. If you've ever declared std::vector<int>, you've instantiated template code with no side effects. std::vector must destruct all its elements when releasing itself, in case you stored a class for type T. This requires, at some point, a statement similar to ptr->~T(); to invoke the destructor. int has no destructor though, so the call has no side effects and will be removed entirely by the optimizer. It's also likely it will be inside a loop, then the entire loop has no side effects, so the entire loop is removed by the optimizer.
So if you disallowed expressions with no side effects, std::vector<int> wouldn't work, for one.
Another common case is assert(a == b). In release builds you want these asserts to disappear - but you can't re-define them as an empty macro, otherwise statements like if (x) assert(a == b); suddenly put the next statement in to the if statement - a disaster! In this case assert(x) can be redefined as ((void)0), which is a statement that has no side effects. Now the if statement works correctly in release builds too - it just does nothing.
These are just two common cases. There are many more you probably don't know about. So, while expressions with no side effects seem redundant, they're actually functionally important. An optimizer will remove them entirely so there's no performance impact, too.
This question already has answers here:
When do extra parentheses have an effect, other than on operator precedence?
(2 answers)
Closed 3 years ago.
Usually auto-generated c++ "main" function has at the end
return (0);
or
return (EXIT_SUCCESS);
But why there are parentheses in above statements? Is it related to C language or something?
// EDIT
I know this is correct, but someone put these brackets for a reason. What's the reason?!
They are not required (even in c). I think some people use them to make 'return' look more like a function call, thinking it is more consistent.
Edit: It's likely the generator does this for it's own reasons. It may be safer or easier for it to work this way.
But why there are parentheses in above
statements? Is it related to C
language or something?
No. As far as I can tell, C has never required parentheses for return statements. This appears to have been the case even before the first ANSI C standard.
This is actually a very interesting question, however, as I've seen that style prevalent among certain C programmers.
I think the most likely guess as to why this style came about is because all other branching statements (for, while, if, switch) require parentheses around expressions. People might have been unaware that they could omit the parentheses for return statements or were aware of this but wanted to achieve a more uniform look to their code.
The ternary ?: operator is sort of an exception as it is an operator and doesn't require parentheses around the conditional expression, yet people often write parentheses there as well regardless of whether it's needed. Some might find it serves to 'group' an expression into a single unit visually.
My second best guess is that this style was influenced by other languages popular at the time. However, popular, procedural alternatives at the time like Pascal did not require that syntax either (Pascal did not even have return values in the C sense but only output parameters) so if this is the case, I'm not aware of any particularly language from which this style originated.
[Subjective] I prefer styles that require the least amount of superfluous decoration to the code whether it comes to naming conventions or how to format it or whether to use additional parentheses where unnecessary. I find that any such decoration tends to be a matter of unique personal preference and falling in love with one way of decorating code just means that someday you'll have to deal with a completely different way (unless you work strictly alone, in which case I envy you). [/Subjective]
This is actually a requirement for BSD kernel source file style.
man 9 style says:
Space after keywords (if, while, for, return, switch).
and
Values in return statements should be enclosed in parentheses.
As any valid expression can be passed to return, those brackets can be added if desired. It is like doing this:
int i = (0);
You can nest an expression in as many brackets as you like:
return (((((0)))));
Things changes with the use of decltype(auto) in c++14 to deduce return type. If parentheses are used then returned type is deduced to be a reference:
decltype(auto) foo1() {
int n;
return (n); // parentheses causes return type to be int&
}
decltype(auto) foo2() {
int n;
return n; // no parentheses causes return type to be int
}
template<typename T> struct TD;
int main()
{
// main.cpp:19:22: error: aggregate 'TD<int&()> f1' has incomplete type and cannot be defined TD<decltype(foo1)> f1;
TD<decltype(foo1)> f1;
// main.cpp:20:22: error: aggregate 'TD<int()> f2' has incomplete type and cannot be defined TD<decltype(foo2)> f2;
TD<decltype(foo2)> f2;
}
There's a dumb reason - to make return look more like a function call.
There's a smarter reason - if the code is generated, code generators often "play it safe" by putting parentheses around expressions just so they never have to be concerned about the precedence leaking out.
Just my silly speculation:
#define RETURN(val) { if (val) printf("Main Exit With Error %d\n", val); return val; }
int main(argc, argv)
{
...
RETURN (E_FILENOTFOUND);
}
Those are not required. Maybe they come from the tendency to prefer more brackets to fewer brackets when writing macros.
Since you mention auto-generated code it might happen that the code used for generating macros was written by reusing code for generating macros or by a person who thought that more brackets won't hurt, but fewer brackets can be fatal.
One reason I can see for this:
return (ERROR_SUCCESS);
is that it's expressing the concept that ERROR_SUCCESS is opaque. We all know it's 0L, but we shouldn't have to.
That's a fairly weak reason.
Another is that it's aesthetically pleasing to use parentheses for consistency, another weak reason.
So in other words, I don't use it myself, but wouldn't flip out if someone else did. :)