0

Edit: Godbolt link for example here!

So I've got this example to show the macros I've made and their use case:

#include <bitset>
#include <iostream>

#define bit_mask(size, offset) (~(~0 << size) << offset)
#define bit_masked_set(dst, src, mask, offset) (dst = (dst & ~mask) | (src << offset))
#define bit_masked_get(src, mask, offset) ((src & mask) >> offset)

constexpr unsigned front_mask = bit_mask(16, 16);
constexpr unsigned back_mask = bit_mask(16, 0);

int main() {
    std::cout << std::bitset<32>(front_mask) << " = front_mask (bit_mask(16, 16))\n";
    std::cout << std::bitset<32>(back_mask) << " = back_mask (bit_mask(16, 0))\n\n";

    unsigned value = 0;
    std::cout << std::bitset<32>(value) << " = value (initial)\n\n";

    bit_masked_set(value, 1, front_mask, 16);
    std::cout << std::bitset<32>(value) << " = value (after bit_masked_set(value, 1, front_mask, 16))\n\n";

    bit_masked_set(value, 1, back_mask, 0);
    std::cout << std::bitset<32>(value) << " = value (after bit_masked_set(value, 1, back_mask, 0))\n\n";

    std::cout << std::bitset<32>(value) << " = value (final)\n";
}

Which outputs:

11111111111111110000000000000000 = front_mask (bit_mask(16, 16))
00000000000000001111111111111111 = back_mask (bit_mask(16, 0))

00000000000000000000000000000000 = value (initial)

00000000000000010000000000000000 = value (after bit_masked_set(value, 1, front_mask, 16))

00000000000000010000000000000001 = value (after bit_masked_set(value, 1, back_mask, 0))

00000000000000010000000000000001 = value (final)

I'm using Visual Studio 2017 with -Wall -c++17 and its happy with this. But I noticed when I pasted it into the godbolt site, gcc/clang/etc give warnings and or errors over things such as:

error: constexpr variable 'front_mask' must be initialized by a constant expression
...
error: left operand of shift expression '(-1 << 16)' is negative [-fpermissive]
...
etc

As these are pre-processor macros that use constant values being assigned to a known datatype shouldn't this not be a problem.

Edit2:

I was playing around with this more and found that changing my bit_mask macro to:

#define bit_mask(size, offset) (~((unsigned long long)~0 << size) << offset)

seemed to fix the problem. I'm not 100% sure if I need the extra long long but I'm assuming it wouldn't hurt, it anything, it would be more flexible.

Edit3:

Significantly updated code from macros to a simple struct:

#include <bitset>
#include <iostream>
#include <type_traits>

struct BitMask {
    using Mask_Type = unsigned long long;

    Mask_Type mask;
    unsigned offset;

    constexpr BitMask(unsigned size, unsigned offset) :
        mask{~((Mask_Type)~0 << size) << offset},
        offset{offset} {
    }

    template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
    constexpr T set_bits(T& dst, T src) const {
        return dst = (dst & ~mask) | (src << offset);
    }

    template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
    constexpr T get_bits(T src) const {
        return (src & mask) >> offset;
    }
};

constexpr BitMask front_mask = BitMask(16, 16);
constexpr BitMask back_mask = BitMask(16, 0);

int main() {
    std::cout << std::bitset<32>(front_mask.mask) << " = front_mask (bit_mask(16, 16))\n";
    std::cout << std::bitset<32>(back_mask.mask) << " = back_mask (bit_mask(16, 0))\n\n";

    unsigned value = 0;
    std::cout << std::bitset<32>(value) << " = value (initial)\n\n";

    front_mask.set_bits(value, 1U);
    std::cout << std::bitset<32>(value) << " = value (after bit_masked_set(value, 1, front_mask, 16))\n\n";

    back_mask.set_bits(value, 1U);
    std::cout << std::bitset<32>(value) << " = value (after bit_masked_set(value, 1, back_mask, 0))\n\n";

    std::cout << std::bitset<32>(value) << " = value (final)\n";
}
Hex Crown
  • 753
  • 9
  • 22
  • Only (correct) errors about the negative value [here](http://coliru.stacked-crooked.com/a/c71cd6f9ada61da4). – πάντα ῥεῖ Jun 23 '19 at 08:11
  • @πάνταῥεῖ Sorry I'm not really sure what is wrong with what im doing, as it works correctly for me. Could you give an example as to the proper way of doing the same thing? – Hex Crown Jun 23 '19 at 08:16
  • Look up 2's complement, e.g. here: https://stackoverflow.com/questions/28233888/is-2s-complement-a-way-to-store-negative-number – πάντα ῥεῖ Jun 23 '19 at 08:19
  • @πάνταῥεῖby changing my `bit_mask` macro to `(~((unsigned)~0 << size) << offset)` all of the errors went away, does this mean its now safe to use? – Hex Crown Jun 23 '19 at 08:20
  • 1
    Not related to your issue, but I would replace your macros for `constexpr` functions. – super Jun 23 '19 at 08:28
  • Don't play with signed integers bitwise. Use unsigned integers instead. – L. F. Jun 23 '19 at 09:16
  • @L.F. you talking with the updated example and how I allow T as the function types? – Hex Crown Jun 23 '19 at 09:19
  • @HexCrown Sorry, I was looking at the example prior to update. – L. F. Jun 23 '19 at 09:21

1 Answers1

0

So since #define bit_mask(size, offset) (~((unsigned long long)~0 << size) << offset) seems like it fixed the problem, I'll mark this as answered. If anyone posts that this wasn't the proper solution, and they provide something different, I'll accept their answer instead, provided it works.

Hex Crown
  • 753
  • 9
  • 22