2016-12-28 9 views
8

C++ 14 добавлена ​​возможность использовать бинарные литералы, введя 0b префикс для значения:Было ли предложение добавить std :: bin в стандарт C++?

int v = 0b1111; // 15 in decimal 

Но нет std::bin манипулятора для потоков, как std::hex или std::oct. Поэтому мне нужно использовать, например. std::bitset для печати цели:

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

ли это было предложено или считать? Если да, то каков статус идеи?

+4

Возможно, просто забыл/никто не нашел времени, чтобы написать и защитить бумагу. – MikeMB

+0

Печатные номера в двоичном формате используются не очень часто. – alain

+0

@alain же для восьмеричного ... Я думаю, – alexolut

ответ

5

Насколько я знаю, для добавления флага форматирования для добавления бинарного форматирования и/или манипулятора std::bin не было предложено ни одного предложения. Вы можете проверить предложения по номеру http://www.open-std.org/jtc1/sc22/wg21/docs/papers/. Я уверен, что предложение добавить бинарные литералы не добавило этого средства (быстрый поиск показал N3472, но я не уверен, что это последняя версия статьи).

С технической точки зрения, это может быть нелегко добавить! Различные флаги, как правило, хранятся всего одним словом в классе потока, и есть разные причины использовать все биты. Существующие три настройки (std::ios_base::oct, std::ios_base::dec, std::ios_base::hex) могут быть хорошо сохранены всего в 2 битах. Конечно, три значения оставят одно значение открытым, за исключением того, что это значение обычно принимается для значения по умолчанию, то есть не фиксирует базу при чтении. В результате может потребоваться изменить макет классов потоков или сделать обработку менее эффективной (например, путем использования iword() для хранения дополнительной возможности двоичного форматирования). Я не сделал анализ, есть ли реальная проблема с любой из реализаций (я знаю, что для моей реализации нет ни одного, но я использовал все биты в слове, если я правильно помню).

Если вы хотите поддерживать двоичное форматирование, относительно легко добавить через пользовательский фасет std::num_put<char>. Ниже приведен простой пример. Он не имеет отношения к некоторым параметрам форматирования, которые могут быть желательными, например, для заполнения или разделителей цифр:

#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"; 
} 
+0

Во всех реализациях я проверил, dec, oct и hex - отдельные биты. И, по крайней мере, libstdC++ (и libC++ на 32-битных платформах), похоже, содержит много запасных бит, не уверен в других. –

+0

@MarcGlisse: Я знаю, что [моя реализация] (http://www.dietmar-kuehl.de/cxxrt/) делает * not * использует отдельные биты. По общему признанию, он сохраняет базу, т. Е. Было бы тривиально добавлять манипулятор 'std :: bin'. Моя реализация сбрасывает ряд условий в одно и то же слово, позволяя проверять несколько отдельных условий только с одной ветвью и одним доступом к памяти (и, похоже, у меня на самом деле был один запасной бит для 32-битных слов). –

+0

В стандарте указано, что ['fmtflags' - это битовая маска, а' dec', 'oct' и' hex' (среди других) - это его элементы] (https://timsong-cpp.github.io/cppwp/ios: : fmtflags # 1), что означает, что [они должны эффективно представлять отдельные биты] (https://timsong-cpp.github.io/cppwp/bitmask.types#3) (потому что 'bitand'-ing любые два из них должны давать нуль).Конечно, реализация могла бы использовать другое внутреннее представление, но для этого требуется дополнительная работа для обеспечения перевода в обоих направлениях. –