0

In an effort to learn compound assignment in C++, I created the following code to demonstrate what they do:

int b05;
int b06 = 13;

b05 = 49;
b05 += b06; // b05 = b05 + b06
cout << "(+=) compound assignment: " << b05 << endl;

b05 = 49;
b05 -= b06; // b05 = b05 - b06
cout << "(-=) compound assignment: " << b05 << endl;

b05 = 49;
b05 *= b06; // b05 = b05 * b06
cout << "(*=) compound assignment: " << b05 << endl;

b05 = 49;
b05 /= b06; // b05 = b05 / b06
cout << "(/=) compound assignment: " << b05 << endl;

b05 = 49;
b05 %= b06; // b05 = b05 % b06
cout << "(%=) compound assignment: " << b05 << endl;

b05 = 49;
b05 >>= b06; // b05 = b05 >> b06
cout << "(>>=) compound assignment: " << b05 << endl;

b05 = 49;
b05 <<= b06; // b05 = b05 << b06
cout << "(<<=) compound assignment: " << b05 << endl;

b05 = 49;
b05 &= b06; // b05 = b05 & b06
cout << "(&=) compound assignment: " << b05 << endl;

b05 = 49;
b05 ^= b06; // b05 = b05 ^ b06
cout << "(^=) compound assignment: " << b05 << endl;

b05 = 49;
b05 |= b06; // b05 = b05 | b06
cout << "(|=) compound assignment: " << b05 << endl;

As you can see, I have to reassign the value of 49 to b05 because the previous operation modifies the values.

Is there a way to go around doing this? Or is there a more efficient method to achieve the same output? (I'd appreciate a code example)

4 Answers4

2

You could do something like this with a macro:

#define compound(op) \
    b05 = 49; \
    b05 op b06; \
    std::cout << "(" << #op << ") compound assignment: " << b05 << std::endl;

Then you call it like this:

int b05, b06 = 13;

compound(+=)
compound(/=)
// etc...

What this does, effectively, is text replacement at compile time. In every place you have a compound(...), it will be replaced with the text of the compound macro, with op being replaced by whatever you provided in the brackets (in this case, an operator of some sort).

To see this in action, do g++ -E <codefile> and you'll see the macro (and any includes) having been expanded out.

Sebastian Lenartowicz
  • 4,695
  • 4
  • 28
  • 39
  • 2
    Don#'t recommend macro solutions please. These are mostly inappropriate for writing c++ code. – πάντα ῥεῖ Aug 08 '16 at 23:04
  • Prefer inline functions to macros. Functions have type safety, macros don't. – Thomas Matthews Aug 08 '16 at 23:08
  • Good answer. No doubt someone will come along and post 10 pages of code to achieve the same thing without macros, and claim it's "better" because it didn't use a macro – M.M Aug 08 '16 at 23:11
  • 1
    Ahhhh crap, I didn't mean to start a macro-flamewar. I don't normally use macros in C++ code, and certainly wouldn't recommend this approach in general, but it seemed the best way to resolve the OP's specific conundrum (given that he's basically writing C code at this point). – Sebastian Lenartowicz Aug 08 '16 at 23:13
  • This code takes up so much less space and runs faster. Thanks so much! I will accept it as the answer. **To those recommending against macros:** if you provide a more efficient (as far as length of code & performance when executing) code example, I may accept it as the answer instead. – James E. Prater Aug 08 '16 at 23:15
  • @SebastianLenartowicz How can I get it so that it replaces the `(op)` text in the `cout` with the actual compound assignment being used, so it outputs `(+=) compound assignment` instead of `(op) compound assignment` for all of them? – James E. Prater Aug 09 '16 at 00:54
  • Fixed it. Use the `#` (stringification) preprocessor operator. – Sebastian Lenartowicz Aug 09 '16 at 00:58
  • @SebastianLenartowicz Awesome, now it works perfectly. Thanks so much! – James E. Prater Aug 09 '16 at 00:59
2

First of all, there's no particular reason for your variables to be named b05 and b06, so let's use a and b, it looks less ugly.

Fundamentally you want to make a copy of your pair of values (two ints, 49 and 13) and pass in a copy to the code which will change them.

The simplest solution is that you create 2 extra variables, assign your values to them:

int a0 = 49, b0 = 13;

and then every time you want to use them, you can copy the assignment:

a = a0; b = b0;

This will avoid duplicating constants (you only specify 49 and 13 once), however you still have to duplicate operators that copy variables.

Any further improvements will not let you avoid this copy, it still has to be done prior to every operation, but you can avoid some source code duplication with a few tricks:

(1) Using struct. That will let you encapsulate two values in one:

struct params {
  int a;
  int b;
}

params p0;
p0.a = 49;
p0.b = 13;

params p;

...
p = p0;
p.a += p.b; // p.a = p.a + p.b
cout << "(+=) compound assignment: " << p.b << endl;
...

Now you're repeating just one command p = p0 instead of two.

(2) Using value parameters:

void test1(int a, int b) {
  a += b; cout << "(+=) compound assignment: " << a << endl;
}
void test2(int a, int b) {
  a -= b; cout << "(-=) compound assignment: " << a << endl;
}
...

int main() {
  int a = 49; int b = 13;
  test1(a, b);
  test2(a, b);
  ...
  return 0;
}

While not explicitly assigning a and b, the copy still occurs every time: actual parameters a and b from main are being copied into formal parameter variables of functions, coincidentally also named a and b.

(3) You can avoid duplicating code by using function references. Here is an example with array of anonymous functions for each operation:

typedef int OperationFn(int, int)

struct OperationInfo {
  std::string label;
  OperationFn op;
}

OperationInfo operations[9] = {
  { "+=", [](int a, int b) { return a += b } },
  { "-=", [](int a, int b) { return a -= b } },
  { "*=", [](int a, int b) { return a *= b } },
  { "/=", [](int a, int b) { return a /= b } },
  { "%=", [](int a, int b) { return a %= b } },
  { ">>=", [](int a, int b) { return a >>= b } },
  { "<<=", [](int a, int b) { return a <<= b } },
  { "&=", [](int a, int b) { return a &= b } },
  { "^=", [](int a, int b) { return a ^= b } },
}

int main() {
  for (int i = 0; i < 9; i++) {
    const OperationInfo& oi = operations[i];
    cout << "(" << oi.label << ") compound assignment: ";
    cout << oi.op(49, 13) << endl;
  }
  return 0;
}

The code mentions parameter substitution only once (oi.op(49, 13) line), but it's in a loop, so it will effectively execute all 9 times. While code looks cleaner and more compact, there is no efficiency gain: you still copy both values prior to every test.

One optimization is possible though: you can use a shared const value for the second argument, because it's never modified:

const int b = 13;

and then don't pass b around, just use b.

Note: pardon my typos. I don't have compiler handy so I can't verify this code. It may have minor mistakes. Leave comments if you find them, I'll update my post.

morfizm
  • 633
  • 3
  • 12
1

You've misunderstood compound operations. They only affect the left value of the expression.

#include <iostream>

int main() {
    int b05 = 10; int b06 = 5;
    b05 |= b06;
    std::cout << "b05 " << b05 << ", b06 " << b06 << "\n";
}

http://ideone.com/5nT7pR

Outputs:

b05 15, b06 5

b06 was unmodified.

kfsone
  • 23,617
  • 2
  • 42
  • 74
0

Maybe you could make a function like this:

class Obj
{
public:
    Obj& operator+= (const Obj &) { return *this; }
    Obj& operator-= (const Obj &) { return *this; }
};

void testFunction(std::function<Obj&(Obj *, const Obj &)> functionToTest)
{
  Object object;
  Object otherObject;
  functionToTest(&object, otherObject);
  cout << "(+=) function test: " << object << endl;
}

int main(void)
{
  std::function<Obj&(Obj *, const Obj &)> functionToTest(&Obj::operator+=);
  testFunction(functionToTest);

  std::function<Obj&(Obj *, const Obj &)> functionToTest2(&Obj::operator-=);
  testFunction(functionToTest2);
}

This code isn't 100% correct but it should give you the idea & the principle is sound. Of course, it doesn't work with primitive types.

1stCLord
  • 860
  • 5
  • 14