1

I'm overloading << to print custom objects (in this case, instances of a custom class Vertex). As part of this, I want to print a given integer in binary. I'd prefer for many reasons to case with std::bitset rather than run a for loop, but the problem I'm encountering is that I have a specific size that each binary should be that depends on the instance. Here's the snippet:

std::ostream &
operator<< (std::ostream& os, const Vertex& V) {
    os << "(" << std::bitset<4>(V.signature()) << ") :";
    for (int e=2; e<V.degree(); ++e) {
        os << " [" << e << "]=" << V.neighbor(e) << " ";
    }
    return os;
}

In place of the 4, I really want to put a size_t that depends on V. For example, here's what I tried:

std::ostream &
operator<< (std::ostream& os, const Vertex& V) {
    size_t B = V.degree()-1;
    os << "(" << std::bitset<B>(V.signature()) << ") :";
    for (int e=2; e<V.degree(); ++e) {
        os << " [" << e << "]=" << V.neighbor(e) << " ";
    }
    return os;
}

The error reads "Non-type template argument is not a constant expression". Is there a way to fix this without hard coding the parameter? It's not something that will be known at compile time.

PengOne
  • 48,188
  • 17
  • 130
  • 149

3 Answers3

1

How about a simple function:

string bin_format(int_type value, size_t len);

which you then write to a stream using

out << bin_format(v.signature(), v.degree()-1);

Anything beyond that violates the KISS principle and needs to be carefully justified. E.g. if you manage to repeat that so often that the dynamic allocation in string causes issues, you could either write a function taking the stream and the parameters and write the single bits to the stream or you could return a proxy with an overloaded operator<< where that operator does the formatting and streaming. BTW, you could also turn that function into a template, based on the integer type, so that you can at least put a maximum on the number of bits you need.

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
0

Since there is no way of using the std::bitset<> with a runtime parameter, you should probably write your own "bit-displaying" class/function. Here is an example of such a class that overloads the stream insertion operator<<:

// Display bits at runtime (cannot use std::bitset<> in this case)

#include <algorithm> // for std::reverse
#include <cstddef>   // for std::size_t
#include <iostream>
#include <string>

template<typename T>
class disp_bitset
{
    std::size_t _size;
    T _n;
public:
    explicit disp_bitset(std::size_t size, T n): _size(size), _n(n) {}
    friend std::ostream& operator<<(std::ostream& os, const disp_bitset& rhs)
    {
        T tmp = rhs._n;
        std::string aux;
        for (std::size_t i = 0; i < rhs._size; ++i)
        {
            aux += (tmp & 0x01) + '0';
            tmp = tmp >> 1;
        }
        std::reverse(aux.begin(), aux.end());
        return os << aux;
    }
};

int main()
{
    disp_bitset<std::size_t> x(8, 41); // size_t 41 on 8 bits
    std::cout << x << std::endl;
    std::cout << disp_bitset<char>(16, 'a'); // char 'a' on 16 bits
}
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • This looks like too much overhead with the class, the for loop, and the reverse, but I like the spirit of the idea. – PengOne Apr 07 '15 at 19:56
  • @PengOne I don't see any way of getting rid of the loop (std::bitset also does this). The `reverse` is not essential though. – vsoftco Apr 07 '15 at 19:57
0

This is a total hack, so I'm hoping for something better, but it seems to get the job done for now. Improvements welcome!

std::ostream &
operator<< (std::ostream& os, const Vertex& V) {
    std::string bin = std::bitset<16>(V.signature()).to_string();
    bin.erase(bin.begin(),bin.end()-V.degree()+1);
    os << "(" << bin << ") :";
    for (int e=2; e<V.degree(); ++e) {
        os << " [" << e << "]=" << V.neighbor(e) << " ";
    }
    return os;
}
vsoftco
  • 55,410
  • 12
  • 139
  • 252
PengOne
  • 48,188
  • 17
  • 130
  • 149