How do I improve handling of if/else local variables? - c++

I commonly have code where I want to detect some condition, and if it exists, then process that condition.
The test for the condition extracts certain information that is important to the processing half of the problem.
However, if/else constructs fail to allow one to declare local-to-the-if variables in the if construct itself(1). Hence, I have to put variable declarations before the beginning of the entire if/else chain for all of the various conditions for which information may be extracted.
For example:
else if ((EndsWithToken("d") || EndsWithToken("dsh")) &&
(p1 = find(m_input_line, '[')) &&
(p2 = find(m_input_line, ']')) &&
p2 > p1)
{
// process what we found inside of the [ ] brackets using p1 and p2...
}
This snippet is part of a long chain of if/else's, and the others don't need p1 and p2 - but they might need an integer value that was extracted from the input stream, or a double, or two points, or whatever....
The point is, there is no way that I know of introduce const TCHAR * p1 and p2 locally to the above scope, the only place it is used, that still allows for the overall structure to remain the same.
Is there some C++ language feature or a pattern that might allow for this?
(1) As some have pointed out, it is possible to declare a single variable of fundamental type, if it is the fist thing, and to test it against non-zero only. This is far too limited to be of much use as a general solution to this issue (though the occasional moment could come up where it is useful - but truly rare).

One solution would be to make each case a distinct function, that returns whether or not it succeeded.
This gives you a natural well-localized scope for the variables, and hopefully (assuming) a reasonable compiler can inline them.

You are allowed to define local variables in if and while statements; however, you can only declare a single one per statement, and you can only test the value for non-zero:
...
else if (TCHAR *p1 = find(m_input_line, '['))
{
if (TCHAR *p2 = find(m_input_line, ']'))
{
...
}
}
In this case, you could also just declare TCHAR *p1, *p2; before the chain of if/else statements, I wouldn't see any problems with that.

Similar to unwind's answer: what about using inline functions for the body of the if/then/else?
However, I guess in both cases you might need to pass quite some values.

Extract into a function.
bool validate(const string& line){
auto startIndex = line.find('[');
auto endIndex = line.find(startIndex,']');
return startIndex != -1 && endIndex != -1 && endIndex > startIndex;
}
//....
else if( EndsWith({"d","dsh"}) && validate(m_input_line) ){ ...}
I notice the prefix "m_" so I am guessing this is part of some class. So make private helper functions as necessary to make the code cleaner and easier to understand.

That's not true. You can declare a variable to the scope of the if if you initialize it there.
if ( int i = 0 )
{
i = 1;
}
else
{
i = 2;
}
This is a dumbed-down example, but it's legal.

Related

Variable declaration in a compound conditional? [duplicate]

I read that in C++17 we can initialize variables in if statements like this
if (int length = 2; length == 2)
//execute something
Instead of
int length = 2;
if (length == 2)
//do something
Even though it is shorter, it affects the code readability (especially for people who don't know this new feature), which I suppose is a bad coding practice for large software development.
Is there any advantage of using this feature other than making the code shorter?
It limits the scope of length to the if alone. So you get the same benefits we originally got when we were allowed to write
for(int i = 0; i < ... ; ++i) {
// ...
}
Instead of the variable leaking
int i;
for(i = 0; i < ... ; ++i) {
// ...
}
Short lived variables are better for several reasons. But to name a couple:
The shorter something lives, the less things you need to keep in mind when reading unrelated lines of code. If i doesn't exist outside the loop or if statement, then we don't need to mind its value outside of them. Nor do we need to worry its value will interact with other parts of the program that are outside of its intended scope (which may happen if i above is reused in another loop). It makes code easier to follow and reason about.
If the variable holds a resource, then that resource is now held for the shortest period possible. And this is without extraneous curly braces. It's also made clear the resource is related to the if alone. Consider this as a motivating example
if(std::lock_guard _(mtx); guarded_thing.is_ready()) {
}
If your colleagues aren't aware of the feature, teach them! Appeasing programmers who don't wish to learn is a poor excuse to avoid features.
Is there any advantage of using this feature other than making the code shorter?
You reduce variable scope. This does make sense and increases readability, as it strengthens the locality of identifiers you need to reason about. I agree that long init statements inside if statements should be avoided, but for short stuff, it's fine.
Note that you can already do initialization and branching on the result in pre-C++17:
int *get(); // returns nullptr under some condition
if (int *ptr = get())
doStuff();
This is subject to one's personal opinion, but you can consider an explicit condition more readable:
if (int *ptr = get(); ptr != nullptr)
doStuff();
Besides, arguing against the readability of a feature by referring to the fact that people aren't used to it is dangerous. People weren't used to smart pointers at some point, yet still we all agree today (I guess) that it's a good thing they are there.
The new form of the if statement has many uses.
Currently, the initializer is either declared before the statement and
leaked into the ambient scope, or an explicit scope is used. With the
new form, such code can be written more compactly, and the improved
scope control makes some erstwhile error-prone constructions a bit
more robust.
Open Standard Proposal for If statement with initializer
So, in summary, this statement simplifies common code patterns and helps users keep scopes tight.
I hope it helps!
In the interest of minimizing the scope of variables there is an idiom which defines a resource only if it is valid upon creation (for example file stream objects):
if(auto file = std::ifstream("filename"))
{
// use file here
}
else
{
// complain about errors here
}
// The identifier `file` does not pollute the wider scope
Sometimes you want to be able to reverse the logic of that test to make the failure the primary clause and the valid resource the else clause. This was previously not possible. But now we can do:
if(auto file = std::ifstream("filename"); !file)
{
// complain about errors here
}
else
{
// use file here
}
An example might be throwing an exception:
if(auto file = std::ifstream(filename); !file)
throw std::runtime_error(std::strerror(errno));
else
{
// use file here
}
Some people like to code so that a function will abort early on an error and otherwise progress. This idiom puts the abort logic physically above the continuation logic which some people may find more natural.
It is especially useful for logical events. Consider this example:
char op = '-';
if (op != '-' && op != '+' && op != '*' && op != '/') {
std::cerr << "bad stuff\n";
}
Seems a bit rough. Unless you are very familiar with OR, AND with negations, you might need to pause and think about this logic - which is generally poor design. With the if-initialization you can add expressiveness.
char op = '-';
if (bool op_valid = (op == '-') || (op == '+') || (op == '*') || (op == '/'); !op_valid) {
std::cerr << "bad stuff\n";
}
the named variable can be re-used inside the if too. E.g:
if (double distance = std::sqrt(a * a + b * b); distance < 0.5){
std::cerr << distance << " is too small\n";
}
This is great, especially given that the variable is scoped and therefore doesn't pollute the space afterwards.
This is an extension of an existing feature, which aids readability in my experience.
if (auto* ptr = get_something()) {
}
Here we both create the variable ptr and we test against it being non-null. The scope of ptr is limited to where it is valid. It is far, far easier to convince yourself that all use of ptr is valid.
But what if we are talking about something that doesn't convert to bool that way?
if (auto itr = find(bob)) {
}
That doesn't work. But with this new feature we can:
if (auto itr = find(bob); itr != end()) {
}
Add a clause saying "when is this initialization valid".
In essence, this gives us a set of tokens that mean "initialize some expression, and when it is valid, do some code. When it is not valid, discard it."
It has been idiomatic to do the pointer-test trick since C++98. Once you have embraced that, this extension is natural.

Initializing variables in an "if" statement

I read that in C++17 we can initialize variables in if statements like this
if (int length = 2; length == 2)
//execute something
Instead of
int length = 2;
if (length == 2)
//do something
Even though it is shorter, it affects the code readability (especially for people who don't know this new feature), which I suppose is a bad coding practice for large software development.
Is there any advantage of using this feature other than making the code shorter?
It limits the scope of length to the if alone. So you get the same benefits we originally got when we were allowed to write
for(int i = 0; i < ... ; ++i) {
// ...
}
Instead of the variable leaking
int i;
for(i = 0; i < ... ; ++i) {
// ...
}
Short lived variables are better for several reasons. But to name a couple:
The shorter something lives, the less things you need to keep in mind when reading unrelated lines of code. If i doesn't exist outside the loop or if statement, then we don't need to mind its value outside of them. Nor do we need to worry its value will interact with other parts of the program that are outside of its intended scope (which may happen if i above is reused in another loop). It makes code easier to follow and reason about.
If the variable holds a resource, then that resource is now held for the shortest period possible. And this is without extraneous curly braces. It's also made clear the resource is related to the if alone. Consider this as a motivating example
if(std::lock_guard _(mtx); guarded_thing.is_ready()) {
}
If your colleagues aren't aware of the feature, teach them! Appeasing programmers who don't wish to learn is a poor excuse to avoid features.
Is there any advantage of using this feature other than making the code shorter?
You reduce variable scope. This does make sense and increases readability, as it strengthens the locality of identifiers you need to reason about. I agree that long init statements inside if statements should be avoided, but for short stuff, it's fine.
Note that you can already do initialization and branching on the result in pre-C++17:
int *get(); // returns nullptr under some condition
if (int *ptr = get())
doStuff();
This is subject to one's personal opinion, but you can consider an explicit condition more readable:
if (int *ptr = get(); ptr != nullptr)
doStuff();
Besides, arguing against the readability of a feature by referring to the fact that people aren't used to it is dangerous. People weren't used to smart pointers at some point, yet still we all agree today (I guess) that it's a good thing they are there.
The new form of the if statement has many uses.
Currently, the initializer is either declared before the statement and
leaked into the ambient scope, or an explicit scope is used. With the
new form, such code can be written more compactly, and the improved
scope control makes some erstwhile error-prone constructions a bit
more robust.
Open Standard Proposal for If statement with initializer
So, in summary, this statement simplifies common code patterns and helps users keep scopes tight.
I hope it helps!
In the interest of minimizing the scope of variables there is an idiom which defines a resource only if it is valid upon creation (for example file stream objects):
if(auto file = std::ifstream("filename"))
{
// use file here
}
else
{
// complain about errors here
}
// The identifier `file` does not pollute the wider scope
Sometimes you want to be able to reverse the logic of that test to make the failure the primary clause and the valid resource the else clause. This was previously not possible. But now we can do:
if(auto file = std::ifstream("filename"); !file)
{
// complain about errors here
}
else
{
// use file here
}
An example might be throwing an exception:
if(auto file = std::ifstream(filename); !file)
throw std::runtime_error(std::strerror(errno));
else
{
// use file here
}
Some people like to code so that a function will abort early on an error and otherwise progress. This idiom puts the abort logic physically above the continuation logic which some people may find more natural.
It is especially useful for logical events. Consider this example:
char op = '-';
if (op != '-' && op != '+' && op != '*' && op != '/') {
std::cerr << "bad stuff\n";
}
Seems a bit rough. Unless you are very familiar with OR, AND with negations, you might need to pause and think about this logic - which is generally poor design. With the if-initialization you can add expressiveness.
char op = '-';
if (bool op_valid = (op == '-') || (op == '+') || (op == '*') || (op == '/'); !op_valid) {
std::cerr << "bad stuff\n";
}
the named variable can be re-used inside the if too. E.g:
if (double distance = std::sqrt(a * a + b * b); distance < 0.5){
std::cerr << distance << " is too small\n";
}
This is great, especially given that the variable is scoped and therefore doesn't pollute the space afterwards.
This is an extension of an existing feature, which aids readability in my experience.
if (auto* ptr = get_something()) {
}
Here we both create the variable ptr and we test against it being non-null. The scope of ptr is limited to where it is valid. It is far, far easier to convince yourself that all use of ptr is valid.
But what if we are talking about something that doesn't convert to bool that way?
if (auto itr = find(bob)) {
}
That doesn't work. But with this new feature we can:
if (auto itr = find(bob); itr != end()) {
}
Add a clause saying "when is this initialization valid".
In essence, this gives us a set of tokens that mean "initialize some expression, and when it is valid, do some code. When it is not valid, discard it."
It has been idiomatic to do the pointer-test trick since C++98. Once you have embraced that, this extension is natural.

In which case is if(a=b) a good idea? [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Inadvertent use of = instead of ==
C++ compilers let you know via warnings that you wrote,
if( a = b ) { //...
And that it might be a mistake that you certainly wanted to write:
if( a == b ) { //...
But is there a case where the warning should be ignored, because it's a good way to use this "feature"?
I don't see any code clarity reason possible, so is there a case where it’s useful?
Two possible reasons:
Assign & Check
The = operator (when not overriden) normally returns the value that it assigned. This is to allow statements such as a=b=c=3. In the context of your question, it also allows you to do something like this:
bool global;//a global variable
//a function
int foo(bool x){
//assign the value of x to global
//if x is equal to true, return 4
if (global=x)
return 4;
//otherwise return 3
return 3;
}
...which is equivalent to but shorter than:
bool global;//a global variable
//a function
int foo(bool x){
//assign the value of x to global
global=x;
//if x is equal to true, return 4
if (global==true)
return 4;
//otherwise return 3
return 3;
}
Also, it should be noted (as stated by Billy ONeal in a comment below) that this can also work when the left-hand argument of the = operator is actually a class with a conversion operator specified for a type which can be coerced (implicitly converted) to a bool. In other words, (a=b) will evaulate to true or false if a is of a type which can be coerced to a boolean value.
So the following is a similar situation to the above, except the left-hand argument to = is an object and not a bool:
#include <iostream>
using namespace std;
class Foo {
public:
operator bool (){ return true; }
Foo(){}
};
int main(){
Foo a;
Foo b;
if (a=b)
cout<<"true";
else
cout<<"false";
}
//output: true
Note: At the time of this writing, the code formatting above is bugged. My code (check the source) actually features proper indenting, shift operators and line spacing. The <'s are supposed to be <'s, and there aren't supposed to be enourmous gaps between each line.
Overridden = operator
Since C++ allows the overriding of operators, sometimes = will be overriden to do something other than what it does with primitive types. In these cases, the performing the = operation on an object could return a boolean (if that's how the = operator was overridden for that object type).
So the following code would perform the = operation on a with b as an argument. Then it would conditionally execute some code depending on the return value of that operation:
if (a=b){
//execute some code
}
Here, a would have to be an object and b would be of the correct type as defined by the overriding of the = operator for objects of a's type. To learn more about operator overriding, see this wikipedia article which includes C++ examples: Wikipedia article on operator overriding
while ( (line = readNextLine()) != EOF) {
processLine();
}
You could use to test if a function returned any error:
if (error_no = some_function(...)) {
// Handle error
}
Assuming that some_function returns the error code in case of an error. Or zero otherwise.
This is a consequence of basic feature of the C language:
The value of an assignment operation is the assigned value itself.
The fact that you can use that "return value" as the condition of an if() statement is incidental.
By the way, this is the same trick that allows this crazy conciseness:
void strcpy(char *s, char *t)
{
while( *s++ = *t++ );
}
Of course, the while exits when the nullchar in t is reached, but at the same time it is copied to the destination s string.
Whether it is a good idea, usually not, as it reduce code readability and is prone to errors.
Although the construct is perfectly legal syntax and your intent may truly be as shown below, don't leave the "!= 0" part out.
if( (a = b) != 0 ) {
...
}
The person looking at the code 6 months, 1 year, 5 years from now, at first glance, is simply going to believe the code contains a "classic bug" written by a junior programmer and will try to "fix" it. The construct above clearly indicates your intent and will be optimized out by the compiler. This would be especially embarrassing if you are that person.
Your other option is to heavily load it with comments. But the above is self-documenting code, which is better.
Lastly, my preference is to do this:
a = b;
if( a != 0 ) {
...
}
This is about a clear as the code can get. If there is a performance hit, it is virtually zero.
A common example where it is useful might be:
do {
...
} while (current = current->next);
I know that with this syntax you can avoid putting an extra line in your code, but I think it takes away some readability from the code.
This syntax is very useful for things like the one suggested by Steven Schlansker, but using it directly as a condition isn't a good idea.
This isn't actually a deliberate feature of C, but a consequence of two other features:
Assignment returns the assigned value
This is useful for performing multiple assignments, like a = b = 0, or loops like while ((n = getchar()) != EOF).
Numbers and pointers have truth values
C originally didn't have a bool type until the 1999 standard, so it used int to represent Boolean values. Backwards compatibility requires C and C++ to allow non-bool expressions in if, while, and for.
So, if a = b has a value and if is lenient about what values it accepts, then if (a = b) works. But I'd recommend using if ((a = b) != 0) instead to discourage anyone from "fixing" it.
You should explicitly write the checking statement in a better coding manner, avoiding the assign & check approach. Example:
if ((fp = fopen("filename.txt", "wt")) != NULL) {
// Do something with fp
}
void some( int b ) {
int a = 0;
if( a = b ) {
// or do something with a
// knowing that is not 0
}
// b remains the same
}
But is there a case where the warning
should be ignored because it's a good
way to use this "feature"? I don't see
any code clarity reason possible so is
there a case where its useful?
The warning can be suppressed by placing an extra parentheses around the assignment. That sort of clarifies the programmer's intent. Common cases I've seen that would match the (a = b) case directly would be something like:
if ( (a = expression_with_zero_for_failure) )
{
// do something with 'a' to avoid having to reevaluate
// 'expression_with_zero_for_failure' (might be a function call, e.g.)
}
else if ( (a = expression2_with_zero_for_failure) )
{
// do something with 'a' to avoid having to reevaluate
// 'expression2_with_zero_for_failure'
}
// etc.
As to whether writing this kind of code is useful enough to justify the common mistakes that beginners (and sometimes even professionals in their worst moments) encounter when using C++, it's difficult to say. It's a legacy inherited from C and Stroustrup and others contributing to the design of C++ might have gone a completely different, safer route had they not tried to make C++ backwards compatible with C as much as possible.
Personally I think it's not worth it. I work in a team and I've encountered this bug several times before. I would have been in favor of disallowing it (requiring parentheses or some other explicit syntax at least or else it's considered a build error) in exchange for lifting the burden of ever encountering these bugs.
while( (l = getline()) != EOF){
printf("%s\n", l);
}
This is of course the simplest example, and there are lots of times when this is useful. The primary thing to remember is that (a = true) returns true, just as (a = false) returns false.
Preamble
Note that this answer is about C++ (I started writing this answer before the tag "C" was added).
Still, after reading Jens Gustedt's comment, I realized it was not the first time I wrote this kind of answer. Truth is, this question is a duplicate of another, to which I gave the following answer:
Inadvertent use of = instead of ==
So, I'll shamelessly quote myself here to add an important information: if is not about comparison. It's about evaluation.
This difference is very important, because it means anything can be inside the parentheses of a if as long as it can be evaluated to a Boolean. And this is a good thing.
Now, limiting the language by forbidding =, where all other operators are authorized, is a dangerous exception for the language, an exception whose use would be far from certain, and whose drawbacks would be numerous indeed.
For those who are uneasy with the = typo, then there are solutions (see Alternatives below...).
About the valid uses of if(i = 0) [Quoted from myself]
The problem is that you're taking the problem upside down. The "if" notation is not about comparing two values like in some other languages.
The C/C++ if instruction waits for any expression that will evaluate to either a Boolean, or a null/non-null value. This expression can include two values comparison, and/or can be much more complex.
For example, you can have:
if(i >> 3)
{
std::cout << "i is less than 8" << std::endl
}
Which proves that, in C/C++, the if expression is not limited to == and =. Anything will do, as long as it can be evaluated as true or false (C++), or zero non-zero (C/C++).
About valid uses
Back to the non-quoted answer.
The following notation:
if(MyObject * p = findMyObject())
{
// uses p
}
enables the user to declare and then use p inside the if. It is a syntactic sugar... But an interesting one. For example, imagine the case of an XML DOM-like object whose type is unknown well until runtime, and you need to use RTTI:
void foo(Node * p_p)
{
if(BodyNode * p = dynamic_cast<BodyNode *>(p_p))
{
// this is a <body> node
}
else if(SpanNode * p = dynamic_cast<SpanNode *>(p_p))
{
// this is a <span> node
}
else if(DivNode * p = dynamic_cast<DivNode *>(p_p))
{
// this is a <div> node
}
// etc.
}
RTTI should not be abused, of course, but this is but one example of this syntactic sugar.
Another use would be to use what is called C++ variable injection. In Java, there is this cool keyword:
synchronized(p)
{
// Now, the Java code is synchronized using p as a mutex
}
In C++, you can do it, too. I don't have the exact code in mind (nor the exact Dr. Dobb's Journal's article where I discovered it), but this simple define should be enough for demonstration purposes:
#define synchronized(lock) \
if (auto_lock lock_##__LINE__(lock))
synchronized(p)
{
// Now, the C++ code is synchronized using p as a mutex
}
(Note that this macro is quite primitive, and should not be used as is in production code. The real macro uses a if and a for. See sources below for a more correct implementation).
This is the same way, mixing injection with if and for declaration, you can declare a primitive foreach macro (if you want an industrial-strength foreach, use Boost's).
About your typo problem
Your problem is a typo, and there are multiple ways to limit its frequency in your code. The most important one is to make sure the left-hand-side operand is constant.
For example, this code won't compile for multiple reasons:
if( NULL = b ) // won't compile because it is illegal
// to assign a value to r-values.
Or even better:
const T a ;
// etc.
if( a = b ) // Won't compile because it is illegal
// to modify a constant object
This is why in my code, const is one of the most used keyword you'll find. Unless I really want to modify a variable, it is declared const and thus, the compiler protects me from most errors, including the typo error that motivated you to write this question.
But is there a case where the warning should be ignored because it's a good way to use this "feature"? I don't see any code clarity reason possible so is there a case where its useful?
Conclusion
As shown in the examples above, there are multiple valid uses for the feature you used in your question.
My own code is a magnitude cleaner and clearer since I use the code injection enabled by this feature:
void foo()
{
// some code
LOCK(mutex)
{
// some code protected by a mutex
}
FOREACH(char c, MyVectorOfChar)
{
// using 'c'
}
}
... which makes the rare times I was confronted to this typo a negligible price to pay (and I can't remember the last time I wrote this type without being caught by the compiler).
Interesting sources
I finally found the articles I've had read on variable injection. Here we go!!!
FOR_EACH and LOCK (2003-11-01)
Exception Safety Analysis (2003-12-01)
Concurrent Access Control & C++ (2004-01-01)
Alternatives
If one fears being victim of the =/== typo, then perhaps using a macro could help:
#define EQUALS ==
#define ARE_EQUALS(lhs,rhs) (lhs == rhs)
int main(int argc, char* argv[])
{
int a = 25 ;
double b = 25 ;
if(a EQUALS b)
std::cout << "equals" << std::endl ;
else
std::cout << "NOT equals" << std::endl ;
if(ARE_EQUALS(a, b))
std::cout << "equals" << std::endl ;
else
std::cout << "NOT equals" << std::endl ;
return 0 ;
}
This way, one can protect oneself from the typo error, without needing a language limitation (that would cripple language), for a bug that happens rarely (i.e., almost never, as far as I remember it in my code).
There's an aspect of this that hasn't been mentioned: C doesn't prevent you from doing anything it doesn't have to. It doesn't prevent you from doing it because C's job is to give you enough rope to hang yourself by. To not think that it's smarter than you. And it's good at it.
Never!
The exceptions cited don't generate the compiler warning. In cases where the compiler generates the warning, it is never a good idea.
RegEx sample
RegEx r;
if(((r = new RegEx("\w*)).IsMatch()) {
// ... do something here
}
else if((r = new RegEx("\d*")).IsMatch()) {
// ... do something here
}
Assign a value test
int i = 0;
if((i = 1) == 1) {
// 1 is equal to i that was assigned to a int value 1
}
else {
// ?
}
My favourite is:
if (CComQIPtr<DerivedClassA> a = BaseClassPtr)
{
...
}
else if (CComQIPtr<DerivedClassB> b = BaseClassPtr)
{
...
}

rate my (C++) code: a recursive strstr sans any standard library string functions :)

So, the idea was a write a recursive function that compares two strings to see if string 'prefix' is contained in string 'other', without using any standard string functions, and using pointer arithmetic. below is what i came up with. i think it works, but was curious - how elegant is this, scale 1-10, any obvious funky moves you would have done instead?
thanks.
bool is_prefixR(char* prefix, char* other) {
static int prePos = 0,othPos = 0;
// static int othPos = 0;
bool test;
test = ( *(prefix+prePos) == *(other+othPos)); //checks to see if same
if (!*(prefix+prePos)) { return 1; } //end of recursion
if (!*(other+othPos)) { return 0; }
if (!test) {
othPos++; //move othPos pointer by 1
prePos = 0; //reset the prefix position
return(is_prefixR(prefix, other)); //lets try again
} else { //chars are the same
othPos++; //move othPos pointer by 1
prePos++;
return(is_prefixR(prefix, other)); //lets try again
}
return 0;
}
This won't work correctly on all strings. For example: prefix="aab", other="aaab"
Don't use static variables. The function will fail the second time it's called because of them.
Don't use recursion: for long strings you can get a stack overflow. Use a loop instead.
The name "prefix" is used for a string appearing at the beginning of another - this is not the case here.
It is 1AM and far to late for understanding code, however such a simple function should be really easy to comprehend and your code isn't. Static variables when writing functions are not a good idea because they make it incredibly hard to debug as the function ceases to become stateless. Try passing the values you need to the next function, and if you find you can't, try writing it a different way. You also used prefix in the wrong way. I think you meant substring.
I present two functions below that do what you want and are fairly foolproof with everything except strings that are not null terminated. It is not quite as fast as it could be, as is_substr will continue to try and compare even when other is shorter than sub. You seemed to indicate elegance was the name of the game though, so I avoided all added complexity.
Note: is_substr depends on is_prefix.
bool is_prefix(const char* prefix, const char* other) {
if ( *prefix == 0 ){
return true;
}else if ( *other == 0 || *prefix != *other ){
return false;
}
return is_prefix(++prefix, ++other);
}
bool is_substr(const char* const sub, const char* other) {
if ( *other == 0 ){
return false;
}else if ( is_prefix(sub, other) ){
return true;
}
return is_substr(sub, ++other);
}
Just to give you an idea of the functions output
is_substr("aab", "aaab"); //1
is_substr("ab", "ba"); //0
is_substr("aab", "a"); //0
is_substr("a", "bab"); //1
is_prefix("a", "a"); //1
is_prefix("a", "ab"); //1
is_prefix("ab", "a"); //0
is_prefix("aab", "aaab"); //0
I'd say its elegance is 0 compared to size_t pos = string1.find(string2);
The standard library is there for a reason. Use it. Even if you wanted to create strstr, which is a C function and not a C++ function, you really don't need to avoid using strcmp. And even then, implementing strcmp yourself in a separate function is going to make this clearer.
On top of that, your static variables make this function only work once. You're doing it wrong.
From your wording, it sounds like this may be a homework question? In which case you should add the homework tag, and say exactly what the requirements are.
The use of
static int prePos = 0,othPos = 0;
makes this function not thread safe. Are you sure that's what you want?
othPos will grow forever, so this function only works once, after the first
time othPos won't begin with a valuie of 0.
I think if you are going to be using pointers, then you should be incrementing the pointers and passing modified pointers around rather than passing the base pointer down and having all of the work happen in the static ints.
Maybe remove all the unecessary parens? But even then I would give it zero in the elegance stakes. The first thing an elegant function must do is to perform one task, and your description of the function - "compares two strings to see if string 'prefix' is contained in string 'other'" simply doesn't make any sense. a prefix must begin a string, not be included in it.
//psuedocode
if prefix == 0
return true
else if other == 0
return false
else return (*prefix == *other) && is_prefix(1+prefix, 1+other);
If you want to use recursion (bad idea, but let's suspend judgement for the moment), you can do it much more cleanly than that:
bool is_prefixR(char* prefix, char* other) {
if (!*prefix) return true;
if (*prefix != *other) return false;
return is_PrefixR(prefix+1, other+1);
}

Declaring and initializing a variable in a Conditional or Control statement in C++

In Stroustrup's The C++ Programming Language: Special Edition (3rd Ed), Stroustrup writes that the declaration and initialization of variables in the conditionals of control statements is not only allowed, but encouraged. He writes that he encourages it because it reduces the scope of the variables to only the scope that they are required for. So something like this...
if ((int i = read(socket)) < 0) {
// handle error
}
else if (i > 0) {
// handle input
}
else {
return true;
}
...is good programming style and practice. The variable i only exists for the block of if statements for which it is needed and then goes out of scope.
However, this feature of the programming language doesn't seem to be supported by g++ (version 4.3.3 Ubuntu specific compile), which is surprising to me. Perhaps I'm just calling g++ with a flag that turns it off (the flags I've called are -g and -Wall). My version of g++ returns the following compile error when compiling with those flags:
socket.cpp:130: error: expected primary-expression before ‘int’
socket.cpp:130: error: expected `)' before ‘int’
On further research I discovered that I didn't seem to be the only one with a compiler that doesn't support this. And there seemed to be some confusion in this question as to exactly what syntax was supposedly standard in the language and what compilers compile with it.
So the question is, what compilers support this feature and what flags need to be set for it to compile? Is it an issue of being in certain standards and not in others?
Also, just out of curiosity, do people generally agree with Stroustrup that this is good style? Or is this a situation where the creator of a language gets an idea in his head which is not necessarily supported by the language's community?
It is allowed to declare a variable in the control part of a nested block, but in the case of if and while, the variable must be initialized to a numeric or boolean value that will be interpreted as the condition. It cannot be included in a more complex expression!
In the particular case you show, it doesn't seem you can find a way to comply unfortunately.
I personally think it's good practice to keep the local variables as close as possible to their actual lifetime in the code, even if that sounds shocking when you switch from C to C++ or from Pascal to C++ - we were used to see all the variables at one place. With some habit, you find it more readable, and you don't have to look elsewhere to find the declaration. Moreover, you know that it is not used before that point.
Edit:
That being said, I don't find it a good practice to mix too much in a single statement, and I think it's a shared opinion. If you affect a value to a variable, then use it in another expression, the code will be more readable and less confusing by separating both parts.
So rather than using this:
int i;
if((i = read(socket)) < 0) {
// handle error
}
else if(i > 0) {
// handle input
}
else {
return true;
}
I would prefer that:
int i = read(socket);
if(i < 0) {
// handle error
}
else if(i > 0) {
// handle input
}
else {
return true;
}
I consider it a good style when used with possibly NULL pointer:
if(CObj* p = GetOptionalValue()) {
//Do something with p
}
This way whether p is declared, it is a valid pointer. No dangling pointer access danger.
On the other hand at least in VC++ it is the only use supported (i.e. checking whether assignment is true)
I use const as much as possible in these situations. Instead of your example, I would do:
const int readResult = read(socket);
if(readResult < 0) {
// handle error
}
else if(readResult > 0)
{
// handle input
}
else {
return true;
}
So although the scope isn't contained, it doesn't really matter, since the variable can't be altered.
They are fixing this in c++17 :
if (int i = read(socket); i < 0)
where if can have an initializer statement.
see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0305r0.html
I've run into a similar problem:
The problem seems to be the parentheses around the int declaration. It should work if you can express the assignment and test without them, i.e.
if (int i = read(socket)) {
should work, but that means the test is != 0, which is not what you want.
While you can use a declaration as a boolean expression, you cannot place a declaration in the middle of an expression. I cannot help thinking that you are mis-reading what Bjarne is saying.
The technique is useful and desirable mostly for control variables of for loops, but in this instance I believe is ill-advised and does not serve clarity. And of course it does not work! ;)
if( <type> <identifier> = <initialiser> ) // valid, but not that useful IMO
if( (<type> <identifier> = <initialiser>) <operator> <operand> ) // not valid
for( <type> <identifier> = <initialiser>;
<expression>;
<expression> ) // valid and desirable
In your example you have called a function with side effects in a conditional, which IMO is a bad idea regardless of what you might think about declaring the variable there.
Adding to what RedGlyph and Ferruccio said.
May be we can do the following to still declare within a conditional statement to limit its use:
if(int x = read(socket)) //x != 0
{
if(x < 0) //handle error
{}
else //do work
{}
}
else //x == 0
{
return true;
}
To complement the other folks' good answers, you can always limit the scope of the variable by braces:
{
const int readResult = read(socket);
if(readResult < 0) {
// handle error
}
else if(readResult > 0)
{
// handle input
}
else {
return true;
}
}
While not directly related to the question, all of the examples put the error handling first. Since there are 3 cases (>0 -> data, ==0 -> connection closed and <0 -> error), that means that the most common case of getting new data requires two tests. Checking for >0 first would cut the expected number of tests by almost half. Unfortunately the "if(int x = read(socket))" approach given by White_Pawn still requires 2 tests for the case of data, but the C++17 proposal could be used to test for >0 first.