15

I wrote this code snippet to convert a two-state string ("+++--+-" or "yynnny") into a std::bitset:

#include <bitset>
#include <cstddef>
#include <string>
#include <iostream>

std::bitset<70> convertTwoStateString(std::string twoState)
{
    std::bitset<70> a{0b0};
    std::bitset<70> eins{0b1};

    for(const auto c : twoState) {
        if(c == '+') {
            a <<= 1;
            a |= eins;
        }
        if(c == '-') {
            a <<= 1;
        }
    }
    return a;
}


int main()
{
    std::string s{"-+--+++--+--+"};
    std::bitset<70> set = convertTwoStateString(s);

    std::cout << set << std::endl;
    //0000000000000000000000000000000000000000000000000000000000100111001001
}

Is there a more algorithmic and/or elegant way to do such a conversion?

Suslik
  • 929
  • 8
  • 28

2 Answers2

35

The std::bitset constructor can specify alternate characters representing 0/1, so you can

std::string s{"-+--+++--+--+"};
std::bitset<70> set(s, 0, s.size(), '-', '+');

Demo

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
3

Fun fact, bitset is not needed and the whole parsing can even be done at compile time if you want.

#include <cstdint>
#include <utility>
#include <stdexcept>
#include <type_traits>

template<std::size_t N>
static constexpr auto parse_bin(const char zero, const char one, const char(&string)[N]) 
{
    static_assert(N <= 8 * sizeof(std::uint32_t));
    static_assert(N > 1);

    std::uint32_t retval{};
    std::uint32_t mask = 1 << (N-2);
    std::size_t index{0ul};
   
    while(string[index] != 0)
    {
        auto c = string[index];
        if ((c != zero) && (c != one))
        {
            throw std::invalid_argument{"invalid input character"};
        }

        if (c == one) retval |= mask;
        mask >>= 1;
        ++index;
    }
    return retval;
}

int main()
{
    static_assert(5 == parse_bin('0', '1', "101"));
    static_assert(9 == parse_bin('-', '+', "+--+"));
    static_assert(17 == parse_bin('n', 'y', "ynnny"));

    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19
  • 4
    From C++23 onward, most (all?) of `std::bitset` functions (including constructors) are `constexpr` ;) – YSC Aug 30 '23 at 07:57
  • Thank you for your interesting answer. But I need std::bitset's anyway since I want to apply in the next steps the binary logic operations. – Suslik Aug 30 '23 at 08:02
  • 1
    @Suslik eh.. tbh there is nothing worse than `bitset` for bulk bitwise operations. Wait, maybe `vector` is worse. bitset was dsinged as a simple converter and accumulator of bit sequence, but otherwise? You always have either to convert to integral type or build own algorythms which iterate through a bitset.. while a CPU would use a single instruction. – Swift - Friday Pie Aug 30 '23 at 08:09
  • 5
    @Swift-FridayPie You're assuming a lot on OP's case: bulk operations, performance requirements, converts to unsigned at the end. – YSC Aug 30 '23 at 08:34
  • @YSC always nice to see more constexpr ;) – Pepijn Kramer Aug 30 '23 at 10:55
  • @OP you can always assign the calculated value to your bitset again. – Pepijn Kramer Aug 30 '23 at 10:55
  • 1
    @YSC I considered making the return type a template argument too but decided to keep it simple for this example (shouldn't be so hard to change this code for that though) – Pepijn Kramer Aug 30 '23 at 10:56