I need to place two dependent init statements within one if condition. As a raw example:
if (bool x = false; bool y = true) std::cout << "Check!\n";
The whole expression evaluates to true, and that is the problem. Suppose I want to test a pointer in the first statement and dereference this pointer to test something else in the second statement:
if (auto ptr = ptr_to_check; auto sth_else = ptr_to_check->sth_else()) { /* do something */ }
And this one is going to crash as I can still dereference nullptr. I want to avoid nested if's as in my program there are other statements nested within this one. Can I place these two statements within one condition somehow?
When it comes to several statements within one if, only the last statement is evaluated as a condition. In this case, a ternary operator is a solution:
if (bool x = false; bool y = x ? true : false) std::cout << "Check!\n";
So, in case of pointers:
if (auto ptr = ptr_to_check; auto sth_else = ptr_to_check ? ptr_to_check->sth_else() : sth_evaluating_to_false) { /* do something */ }
Related
I have an atomic type where I need to atomically compare it with a value, and if the two values are not equal then exchange the value of the atomic.
Put another way, where compare_exchange_strong essentially does this operation atomically:
if (atomic_value == expected)
atomic_value = desired;
...I'm looking for a way to do this:
if (atomic_value != expected)
atomic_value = desired;
(Yes, I know compare_exchange_strong compares using bitwise equality, not the == operator. And I know the value of expected gets assigned when the comparison fails. This was just for illustration purposes. In my use case I don't need the value of the atomic regardless of the result of the comparison.)
Is there any way to do this without having to fall back on using a lock instead of std::atomic?
auto observed = atomic_value.load();
for (;;)
{
if (observed == expected){
break; // no exchange
}
if (atomic_value.compare_exchange_weak(observed, desired)) {
break; // successfully exchanged observed with desired
}
}
Sure it is suboptimal on architectures where HW has LL/SC, as C++ does not expose it. With LL/SC can have arbitrary condition.
You could use something like this:
#include <atomic>
#include <random>
std::atomic<int> atomVal;
int store_if_not_equal(int desired)
{
while (true) // or maxloop....
{
int expected = desired;
if (atomVal.compare_exchange_strong(expected, desired))
{
// values matched - do nothing
return 0;
}
else
{
//expected now contains the "current value"
// another thread could have sneaked in and changed it,
// so only replace it if it still matches
if (atomVal.compare_exchange_strong(expected, desired))
{
// success
return 1;
}
}
// if we arrive here, retry
}
}
int main()
{
atomVal.store(rand());
return store_if_not_equal(2);
}
Demo: https://godbolt.org/z/qWTP7canf
Just use the loop one ordinarily uses with compare-and-exchange, but instead of looping until the (new) expected value matches, loop either until it matches (and the store happens) or it equals the value in your != expected condition, since that’s the case in which you needn’t do anything. (Obviously don’t make the initial value be that “unless” value so that you can’t “succeed” the first time.)
I have a class Point which has a member method to get position:
class Point {
private:
int x; int y;
public:
Point(int a, int b) {
x = a; y = b;
}
int getX() { return x; }
int getY() { return y; }
};
These are stored in a list<Point> named listPoints. I have a function which checks whether a position matches any of the points in the list:
bool checkMatch(int x, int y) {
for (Point p : listPoints) {
if (p.getX() == x && p.getY() == y) {
return true;
}
}
return false;
}
Note the . is used to access member methods of Point, but there's another way:
bool checkMatch(int x, int y) {
list<Point>::iterator p = listPoints.begin();
for (; p != listPoints.end(); ++p) {
if (p->getX() == x && p->getY() == y) {
return true;
}
}
return false;
}
What is this function doing differently to the one before, specifically why does . no longer work and I need to use -> instead to access member methods of Point? Are these foreach loops fundamentally different?
They're not different no, with some very minor exceptions. In the second loop, you're using an iterator, which is more-or-less a pointer to the object itself. It can be dereferenced to get the actual object.
You'd use iterators if you wanted to remove some elements. So say instead of checking for matches, you were removing anything that matched, you'd want to iterate with iterators.
Since you are just iterating over the entire range, it's far clearer to use your for-ranged loop. It's easier to write and clearer.
specifically why does . no longer work and I need to use -> instead to access member methods of Point?
Because the iterator is an object, which basically points to the actual object. You cannot override the dot operator, so instead operator-> is overridden to retrieve the object. One could also dereference the iterator like *p, which allows you to use the dot operator (*p).getX()
Are these foreach loops fundamentally different?
They are not fundmentally different. They are subtly different.
It's analogous to:
int a;
int* ptr = &a;
a = 10;
*ptr = 10;
The last two lines are not fundmentally different. An iterator is kinda like a pointer. Its operator* is overloaded such that using *p acts as though you are dereferencing a pointer -- you get a reference to an item in the container.
The second block of code can be changed a bit to resemble the first one.
list<Point>::iterator iter = listPoints.begin();
for (; iter != listPoints.end(); ++iter) {
Point& p = *iter;
if (p.getX() == x && p.getY() == y) {
return true;
}
}
Under the covers, the first block is exactly that.
See the documentation on the range-for loop in the standard for the details.
I'm writing a program for an assignment - it's supposed to be a database
for information about employees in a company. Basically, a vector containing
structures (individual employees).
The trouble I'm having is that remove_if erases everything from the vector - instead of an individual employee.
If I understood documentation/other topics correctly, that function should
do two things - rearrange elements of the vector, and return an
iterator to the first element outside the new range - but it doesn't do
it, it returns an iterator to the first element - and so when the
erase() function is called, all elements are deleted. At least
that's what I found when debugging it.
Here's a mcve of my code:
#include <iostream>
#include <vector>
#include <algorithm>
struct employee {
int number;
};
int main()
{
//creating the vector and adding some values to it
employee one{ 1 };
employee two{ 2 };
employee three{ 3 };
std::vector <employee> staff{ one, two, three };
int m = 2; //some parameter I want to pass to lambda function
auto it = std::remove_if(staff.begin(), staff.end(),
[m](employee a) {
if (a.number == 2)
return true; }
);
staff.erase(it, staff.end());
for (auto it = staff.begin(); it != staff.end(); it++)
std::cout << it->number << std::endl;
system("pause");
return 0;
}
I realise that I could've done the same thing in a loop - in fact, I did, but I just can't wrap my head around why doesn't this approach work. Also, a list would've probably been a better choice for this program (with it, the for loop would have taken fewer instructions to compute), but I've already finished the program, and right now I just really want to know why didn't remove_if work.
Thanks!
EDIT: As #drescherjm pointed out, that was due to the fact that the lambda function didn't return false when the when the if statement wasn't met.
So the question is answered.
The main problem is you are not returning a value when your condition in your lambda is not met. This is undefined behavior not to return a value.
auto it = std::remove_if(staff.begin(), staff.end(),
[m](employee a) {
if (a.number == 2)
return true; }
);
A simple solution is to remove the if and just return the conditional.
auto it = std::remove_if(staff.begin(), staff.end(),
[m](employee a) {
return (a.number == 2);
}
);
However as #killzonekid mentioned this is not correct because you are still not using the parameter.
auto it = std::remove_if(staff.begin(), staff.end(),
[m](employee a) {
return (a.number == m);
}
);
Replacing the fixed 2 with m should take care of that.
Looking to achieve a sort of dynamic expression where I can later evaluate the booleans if called.
condition &&= condition2; //not evaluated just yet
condition ||= condition3;
if (condition) //evaluated now
do this;
else
do this;
for example I am the using the same conditions throughout my code and it would be easier if I could just adjust the one statement or add more to it even when the programs running.
conditions = (x>50 && y>200) && (type == MONKEY);
conditions &&= (x<75 && y<250);
and later on in the code
if (conditions)
cout<<"Hello!";
edit: The conditions should be evaluated at the if statement.
Be very careful when working with && and &
Reason 1
Expanding the postulated (and syntactically invalid)
condition &&= condition2;
to
condition = condition && condition2;
reveals a subtlety: condition2 will not be evaluated if condition is false.
Reason 2
& and && also have differing behaviour for integral types, e.g. 0b01 & 0b10 is 0 but 0b01 && 0b10 is true (here I'm using C++14 binary literals).
Conclusion
So I'd favour the compaction
if (condition = condition && condition2){
// do this
} else {
// do this
}
where condition2 is only evaluated if condition is true
The sensible solution here is to create named functions for those conditions and call them whenever necessary.
That said ...
Is it possible [..]
Of course. To defer evaluation, just wrap your conditions in (lambda) functions. Proof of concept:
#include <functional>
#include <iostream>
template<typename F, typename G>
auto and_also (F f, G g) {
return [=]() {
bool first = f();
if (! first) return false;
return static_cast<bool>(g());
};
}
int main () {
int dummy = -1;
std::function<bool()> condition = [&](){return dummy > 0;};
condition = and_also(condition, [&](){return dummy < 42;});
dummy = 21;
if (condition()) std::cout << "in range" << std::endl;
}
In order to address the use case, you've mentioned in a comment:
Example: moving through pixels in a window and avoiding certain areas of the window.
For reasons of readability, I'd recommend you define a function, e.g., like this (pseudocode) example:
bool is_in_rect(point2i p, rect2i rect) {
// check x range
if (p.x >= rect.x1 && p.x < rect.x2)
return true;
// check y range
if (p.y >= rect.y1 && p.y < rect.y2)
return true;
return false;
}
You can add functions for specialized situations as you wish:
bool is_in_monkey_rect(point2i p, rect2i rect) {
return rect.type == MONKEY && is_in_rect(p, rect);
}
But overall I'm just interested to see if this is possible.
Other than delegating to a function, you'd probably use macros to simulate this kind of lazy evaluation. But I wouldn't recommend that.
Note that, depending on the actual nature of the problem, it might make more sense to adapt the iteration pattern, rather than iterating all the data and check every single pixel for relevance.
A common way to do something in one place, so you don't repeat yourself everywhere is to make a function.
For example,
bool special_condition(bool current_condition, int x)
{
return current_condition && (x<75 && y<250);
}
allows this whenever needed.
if (special_condition(conditions))
do_something();
else
do_something_else();
This will be evaulated when the if is encountered, however, this won't short circuit if current_condition is false when the function will still be called.
Short answer Stefan, in C++ no.
The expression is evaluated during &=.
Short circuiting expressions e.g. ignoring b if a is false in expressions such as a && b, is usually configurable in your environment and normally on.
If you still wish to do so, create an evaluation function instead.
/Anders
We have a function like so:
const std::string& ConvertToString(Enum e)
{
static const std::string enumStrings[4] =
{
std::string("first"),
std::string("second"),
std::string("third"),
std::string("unknown")
}
int enumOrd = static_cast<int>(e);
if (e < 0 || e > 2)
{
return enumStrings[3];
}
return enumStrings[enumOrd];
}
Now, the problem is that this function is being called after main() returns, and it's crashing since that static array has already been destroyed. I want to change it to be like so:
std::string ConvertToString(Enum e)
{
switch (static_cast<int>(e))
{
case 0: return std::string("first");
case 1: return std::string("second");
case 2: return std::string("third");
default: return std::string("unknown");
}
}
I wondering if it's possible for this change to break any code which calls this function. I can't think of any problems (as long as the class being returned didn't do sneaky things in const methods, but std::string should be fine, especially in the transition of const ref -> value, but maybe I'm missing something.
One way this could go wrong is if the calling code does something like:
const char* s = ConvertToString(first).c_str();
And then stores s somewhere and accesses it later, thinking that the std::string object referenced by the return value of ConvertToString will never be destroyed.
Of course, that calling code sucks anyway, in that case.
You can break code that depends on different calls to ConvertToString yielding the same object:
for (std::string::const_iterator it = ConvertToString(first).begin();
it != ConvertToString(first).end(); ++it) {
// undefined behavior: cannot compare iterators referring
// to two different containers/strings
}
One other side note, your implementation might not do what you expect. In particular if your enumeration is defined as enum Enum { first, second, third }; the code:
if (e < 0 || e > 2) {
return enumStrings[3];
}
can be completely removed by an optimizing compiler and unknown might never be returned by the function. The reason is that the Enum enumeration cannot legally hold any value that is not the bitmask of the enumerators (in this case: 0, 1, 2, 3). Doing so is undefined behavior. The compiler can use this knowledge to optimize the code and remove the e < 0 and/or convert the test condition into a single e == 3.