3
\$\begingroup\$

I developed a lot of code locally with __uint128_t only to find out that it is not available on the target platform.

I am now trying to write my own struct that emulates the operators of __uint128_t I need: =, ~, ^, &, |, ^=, &=, |=

This is the code I have now, which will be compiled with a C++11 compiler:

struct board_t {
    uint64_t hi;
    uint64_t low;    

    board_t operator~() const {
        return {~hi, ~low};
    }

    board_t operator|(const board_t & rhs) const {
        return {hi | rhs.hi, low | rhs.low};
    }

    board_t& operator|=(const board_t & rhs) {
        hi |= rhs.hi;
        low |= rhs.low;

        return *this;
    }

    // Complete equivalent functions for &, ^, &= ^=
};
  • Will anything behave unexpectedly with this code?
  • Do I need to create a constructor? Is the usage of the {} struct initializer good form?
  • Do I need to override the assignment operator?
  • Are there other ways to improve this code, style, correctness and speed?
\$\endgroup\$
4
\$\begingroup\$

Will anything behave unexpectedly with this code?

For that to answer we would need the expectations. Most users will expect a number type to behave like the ints do.

What does that mean in your example?

  • your type should be usable in an if(number) (this also includes if(!number))
  • your type should be comparable with other int types, nobody wants to write number == board_t{0, 3} instead of number == 3 (this probably means you should write an implicit conversion from int to board_t

Surely there are other things you need to mimic from the ints.

Do I need to create a constructor? Is the usage of the {} struct initializer good form?

Given the fact that you should provide something like board_t(int) you will need to provide the other constructors as well.

Internal functions (that know about the internal format of board_t might use the "struct initialize" but in general I would avoid it as it introduces dependencies on the inner layout.

Do I need to override the assignment operator?

No, the default generated one will simply call the assignments on the members as well which is fine.

Are there other ways to improve this code, style, correctness and speed?

Yes:

  • why is the type called board_t?
  • if the user should be able to supply values you could generate an own literal for this purpose
  • define binary operators like operator+ outside the class to get "more symmetric" code (this makes both operands eligible for automatic conversions instead of only the right hand side)
  • the previous point may also be applied for unary operators that don't change the internal representation of the object (like operator~())

All in all your design should be:

If the platform supports uint128 the type should only be an alias of it and if it does not support this type, then your class should behave as much as uint128 as possible (so on platforms that support it your code should do the same for both "implementations").

\$\endgroup\$
  • \$\begingroup\$ Thanks! I will not use this struct as a number, but to represent a gameboard. Hence no need for the +-/* operators or the need for conversion to smaller/bigger types, and the name board_t. The if(variable) case is indeed something I forgot. I can't find how to do that, any pointers? \$\endgroup\$ – Peter Smit Aug 22 '14 at 9:25
  • \$\begingroup\$ How about this for the bool conversion? \$\endgroup\$ – Nobody Aug 22 '14 at 9:31
  • 2
    \$\begingroup\$ If you really want to represent a board (whatever that means in your context) I would advice you to use std::bitset instead. \$\endgroup\$ – Nobody Aug 22 '14 at 9:33
  • \$\begingroup\$ @LokiAstari: The wording was wrong (and I changed the paragraph now). What I meant was: For types that behave like ints the default copying, which is shallow (and behaves like bitwise copy on builtin types), is usually sufficient. \$\endgroup\$ – Nobody Aug 25 '14 at 10:03
1
\$\begingroup\$

Will anything behave unexpectedly with this code?

The default behavior of constructor/destructor and assignment operator (when no pointers are involved) is what you would expect. So yes it looks like it will behave just fine.

Do I need to create a constructor?

No. The way way you are using struct assignment is fine. BUT personally I would provide one so that you can construct this object from a single short/int/long. That way you can construct from a single integer literal.

Is the usage of the {} struct initializer good form?

It works as you expect. So it looks fine to me.

Do I need to override the assignment operator?

No. The compiler generated one will do what you expect.

Are there other ways to improve this code, style, correctness and speed?

The only change I would make, is to define the operator X in terms of the assignment operator X.

// The assignment version of operator '|'
board_t& operator|=(const board_t & rhs) {
    hi  |= rhs.hi;
    low |= rhs.low;

    return *this;
}

// The normal version of operator '|'
board_t operator|(const board_t & rhs) const {
    return board_t(*this) |= rhs;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.