Has there been a proposal to add std::bin to the c++ standard?
Asked Answered
C

1

28

C++14 adds ability to use binary literals by typing 0b prefix for the value:

int v = 0b1111; // 15 in decimal

But there is no std::bin manipulator for streams like std::hex or std::oct. So I need to use e.g. std::bitset for printing purpose:

std::cout << std::bitset<4>(v) << "\n";

Has it been proposed or considered? If so, what's the status of the idea?

Connelly answered 28/12, 2016 at 9:38 Comment(6)
Was probably just forgotten / no one took the time to write and defend a paper.Reinhard
Printing numbers in binary is not used very often I guess.Kist
@Kist same for octal ... I thinkKaveri
@alexolut, you can do it yourself (apparently here is the info on how to make one).Tobar
@alexolut: Sorry, I didn't want to dismiss that possibility. It was just my best guess (hence not an answer) from experience with similar questions.Reinhard
See also the posting groups.google.com/a/isocpp.org/d/msg/std-proposals/Tga5HwCKDP8/… in the proposal discussions.Harbinger
C
10

As far as I know there was no proposal submitted to add a formatting flag to add binary formatting and/or a manipulator std::bin. You can check the proposals at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/. I'm pretty sure the proposal to add binary literals did not add this facility (a quick search revealed N3472 but I'm not sure if this is the latest version of the paper).

From a technical point of view it may not be entirely easy to add! The various flags are normally all stored in just one word in the stream class and there are various reasons to use up all the bits. The existing three settings (std::ios_base::oct, std::ios_base::dec, std::ios_base::hex) can be nicely stored in just 2 bits. Of course, the three values would leave one value open except that this value is typically taken for the default setting, i.e., not fixing the base when reading. As a result it may be necessary to change the layout of the stream classes or to make processing less efficient (e.g., by somehow using an iword() to store the additional possibility of binary formatting). I haven't done the analysis whether there is an actual problem with any of the implementations (I know there is none for my implementation but I did use all the bits in a word if I recall correctly).

If you want to support binary formatting it is relatively easy to add via a custom std::num_put<char> facet. Below is a simple example. It doesn't deal with some of the formatting options which may be desirable like padding or digit separators:

#include <iostream>
#include <limits>
#include <locale>

class binary_num_put
    : public std::num_put<char> {
    template <typename T>
    iter_type common_put(iter_type out, std::ios_base& str, char_type fill,
                         T original, unsigned long long v) const {
        if (str.flags() & std::ios_base::basefield) {
            return this->std::num_put<char>::do_put(out, str, fill, original);
        }
        if (str.flags() & std::ios_base::showbase) {
            *out++ = '0';
            *out++ = str.flags() & std::ios_base::uppercase? 'B': 'b';
        }
        unsigned long long mask(1ull << (std::numeric_limits<unsigned long long>::digits - 1));
        while (mask && !(mask & v)) {
            mask >>= 1;
        }
        if (mask) {
            for (; mask; mask >>= 1) {
                *out++ = v & mask? '1': '0';
            }
        }
        else {
            *out++ = '0';
        }
        return out;
    }
    iter_type do_put(iter_type out, std::ios_base& str, char_type fill, long v) const {
        return common_put(out, str, fill, v, static_cast<unsigned long>(v));
    }
    iter_type do_put(iter_type out, std::ios_base& str, char_type fill, long long v) const {
        return common_put(out, str, fill, v, static_cast<unsigned long long>(v));
    }
    iter_type do_put(iter_type out, std::ios_base& str, char_type fill, unsigned long v) const {
        return common_put(out, str, fill, v, v);
    }
    iter_type do_put(iter_type out, std::ios_base& str, char_type fill, unsigned long long v) const {
        return common_put(out, str, fill, v, v);
    }
};

std::ostream& bin(std::ostream& out) {
    auto const& facet = std::use_facet<std::num_get<char>>(out.getloc());
    if (!dynamic_cast<binary_num_put const*>(&facet)) {
        std::locale loc(std::locale(), new binary_num_put);
        out.imbue(loc);
    }
    out.setf(std::ios_base::fmtflags(), std::ios_base::basefield);
    return out;
}

int main()
{
    std::cout << std::showbase << bin << 12345 << " "
              << std::dec << 12345 << "\n";
}
Carnotite answered 28/12, 2016 at 11:34 Comment(4)
In all implementations I checked, dec, oct and hex are separate bits. And at least libstdc++ (and libc++ on 32+ bit platforms) seem to have plenty of spare bits, not sure about the others.Bangup
@MarcGlisse: i know that my implementation does not use separate bits. Admittedly, it does store the base, i.e., it would be trivial to add a std::bin manipulator. My implementation folds a number of conditions into the same word to allow checking of multiple separate conditions with just one branch and one memory access (and it seems I actually still had one spare bit for 32 bit words).Wrasse
The standard says that fmtflags is a bitmask type and dec, oct and hex (among others) are its elements, meaning that they must effectively represent separate bits (because bitand-ing any two of them must yield zero). Of course, an implementation could choose to use a different internal representation, but that requires additional work to provide translation both ways.Activity
@T.C.: in my implementation the fmtflags are stored in the same word as a number of state bits. Doing so requires a few extra bitwise operations on [user] access to the flags. In return some checks require just one branch.Wrasse

© 2022 - 2024 — McMap. All rights reserved.