6

Sometimes, it may be useful to build an integral value from a list of bits (in increasing order). Such a function could be named to_integral.

Example:

static_assert (to_integral(1,1,0,1,0,1) == 0b101011);

Is there any such to_integral function in std?

It is quite straightforward to implement to_integral but my intent is not to reinvent the wheel and I might have missed something like that in std.

I need the function to get its input as a variable list, e.g. with a prototype like template<typename...Args> constexpr auto to_integral (Args...args)

6
  • 2
    As types are identical, taking an std::initializer_list<bool> seems more appropriate. Commented Oct 29 at 9:11
  • @Jarod42, good point. It should require extra {} for calling the function, shouldn't it ? Commented Oct 29 at 9:18
  • yes extra {} would be required Commented Oct 29 at 10:45
  • 2
    Short answer - No, the standard library does not have a function that interprets a sequence of ints as bits of an integer. Commented Oct 29 at 10:45
  • 1
    The closest is probably std::from_chars, but it's still a bit involved to use: godbolt.org/z/G7vsbx3qK Commented Oct 29 at 11:34

4 Answers 4

8

You can use bitset (which is constexpr in C++23) like:

#include <climits>
#include <bitset>
#include <ranges>

constexpr auto to_integral(std::integral auto... bit) {
  const auto bits = {(bit ? '1' : '0')...};
  return std::bitset<sizeof(unsigned long long) * CHAR_BIT>
    (std::string(std::from_range, std::views::reverse(bits)))
      .to_ullong();
}

static_assert (to_integral(1,1,0,1,0,1) == 0b101011);

Demo

Sign up to request clarification or add additional context in comments.

Comments

4

I don't know of any std constexpr way to do this. I recently needed the same thing, so here's my C++20 solution for reference:

template<typename R> requires std::integral<R>
[[nodiscard]] constexpr R to_integral(std::integral auto... args)
{
    return []<typename... Args, std::size_t... Is>(std::index_sequence<Is...> seq, Args... args)
    {
        return ((static_cast<R>(args) << Is) | ... | 0);
    }(std::make_index_sequence<sizeof...(args)>(), args...);
}

static_assert (to_integral<int>(1,1,0,1,0,1) == 0b101011);
static_assert (to_integral<int>(1,1,0,1,0,1) == to_integral<std::size_t>(1,1,0,1,0,1));

Note I originally had the parameters of type bool but changed it here to accept any integral type. This doesn't check if any parameter has a value other than 0 or 1.

Comments

3

Since C++23 you can use std::bitset to do that (constexpr constructor since C++11, constexpr to_ulong since C++23):

#include <iostream>
#include <bitset>

int main() {
    constexpr auto x = std::bitset<6>("110101").to_ulong();
    std::cout << x;
}

Live Demo

To get the desired signature, we can construct a constexpr string from the input and then use std::bitset. Parts of this code is taken from a comment from Artyer who initially used from_chars, but it works with bitset as well:

#include <charconv>
#include <array>
#include <bitset>
#include <algorithm>


template<typename T = int>
constexpr T to_integral(auto&&... bits) {
    if (sizeof...(bits) == 0)
        return T{};
    char s[] = { std::forward<decltype(bits)>(bits) ? '1' : '0'... };
    std::ranges::reverse(s);
    return std::bitset<sizeof...(bits)>(s,sizeof...(bits)).to_ulong();
}

static_assert (to_integral(1,1,0,1,0,1) == 0b101011);

Live Demo

3 Comments

Thank you. I was not precise enough in my question but I need the function to have a parameter pack as input rather than an input string (I'm going to update the question)
ok understand. I am going to keep this answer, maybe I'll edit it later
i stole the constexpr string part from Artyers comment above. Now bitset can be used with the desired signature
0

Here is one more solution.

#onclude <iostream>
#include <format>
#include <limits>
#include <concept>
#include <stdexcept>

template <std::integral R = int, std::integral ...Bits>
constexpr R to_integral( Bits ... bits )
{
    if constexpr (std::numeric_limits<R>::digits < sizeof... ( bits ))
    {
        throw std::invalid_argument( "The number of arguments exceeds the number of bits in the result type." );
    }
    else
    {
        R result = 0;

        if constexpr (sizeof...( bits ) != 0)
        {
            size_t i = sizeof...( bits );

            ( result |= ... |= ( static_cast< R >( !!bits ) << --i ) );
        }

        return result;
    }
}

static_assert( to_integral( 1, 1, 0, 1, 0, 1 ) == 0b101011 );
 
int main()
{
    std::cout << std::format( "{:#b}\n", to_integral<int>() );
    std::cout << std::format( "{:#b}\n", to_integral( 1, 1, 0, 1, 0, 1 ) );
    std::cout << std::format( "{:#b}\n", to_integral<unsigned long long>( 1, 1, 0, 1, 0, 1 ) );
}

The program output is

0b0
0b101011
0b101011

The solution is based on two fundamental concepts of the C++ Standard.

The first one is the binary left fold expression. According to the C++ Standard (the C++23 Standard, section "13.7.4 Variadic templates"):

10 The instantiation of a fold-expression (7.5.6) produces:

(10.3) — ( (((E op E1) op E2) op · · · ) op EN ) for a binary left fold, and

And the second one is relative to the assignment and compound assignment operator. According to the C++ Standard (the C++23 Standard, section "7.6.19 Assignment and compound assignment operators"):

1 The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand; their result is an lvalue of the type of the left operand, referring to the left operand. The result in all cases is a bit-field if the left operand is a bit-field. In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression. The right operand is sequenced before the left operand. With respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation.

If you need to build a binary representation in the descending order then just make only two minor changes. They are

    size_t i = 0;

    ( result |= ... |= ( static_cast< R >( !!bits ) << i++ ) );

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.