8
\$\begingroup\$

I've been trying to make an std::vector of boolean values and I got fed up with the specialization. To get around this, I made a wrapper class around bool and a testsuite to make sure that it compiles and works as expected.

It seems to do everything std::vector<bool> does, except, of course, flip(). It also satisfies the container rules so that you can take a reference or a pointer to an element.

On to the code:

vector_safe_bool.hpp:

#pragma once

// A wrapper class around bool that can be used in std::vector without breaking container rules

class vector_safe_bool {
    bool value;
public:
    vector_safe_bool() = default;
    vector_safe_bool(bool b) : value{b} {}

    bool *operator&() noexcept { return &value; }
    const bool *operator&() const noexcept { return &value; }

    operator const bool &() const noexcept { return value; }
    operator bool &() noexcept { return value; }
};

vsb_test.cpp:

#include "vector_safe_bool.hpp"

#include <iostream>
#include <vector>

#if USE_VECTOR_SAFE_BOOL
using which_bool = vector_safe_bool;
#define TEST_CONTAINER true
#else
using which_bool = bool;
#define TEST_CONTAINER false
#endif

int main()
{
// The commented lines work for neither bool nor vector_safe_bool
    const which_bool t1 = true;
//  bool *t1p = &t1;
    const bool *t1cp = &t1;
//  bool &t1r = t1;
    const bool &t1cr = t1;
//  bool &&t1rr = t1;

    which_bool t2 = true;
    bool *t2p = &t2;
    const bool *t2cp = &t2;
    bool &t2r = t2;
    const bool &t2cr = t2;
//  bool &&t2rr = t2;
    t2++;
    ++t2;

    std::vector<which_bool> bv(10, true);
#if TEST_CONTAINER
    for(auto &b : bv)
        ;
#endif
    for(const auto &b : bv)
        ;
    for(auto &&b : bv)
        ;
    const std::vector<which_bool> cbv(10, true);
#if TEST_CONTAINER
    for(auto &cb : cbv)
        ;
#endif
    for(const auto &cb : cbv)
        ;
    for(auto &&cb : cbv)
        ;
}

GNUmakefile:

CXX = g++
CXXFLAGS = -Wall -Wextra -Wno-unused-variable -Wno-deprecated -pedantic -std=c++11

all:

check test: vsb_test.cpp vector_safe_bool.hpp
    $(CXX) $(CXXFLAGS) -I. vsb_test.cpp -DUSE_VECTOR_SAFE_BOOL
    @rm -f a.out
    $(CXX) $(CXXFLAGS) -I. vsb_test.cpp
    @rm -f a.out

What I'm looking for:

  • Can I make this more idiomatic? How?
  • Can I make use of features from newer versions of C++?
  • Did I forget any obscure corner-cases?
  • Is there anything that I can make better in the GNUmakefile?
  • Are there any other ways that I could improve this?
\$\endgroup\$
5
  • \$\begingroup\$ We can make the wrapper as close to bool as possible, but there is some limitations (e.g., decltype, template parameter deduction, etc.). In the end, there is no way to make bool itself usable with std::vector, so unfortunately sometimes libraries have to reinvent std::vector :( \$\endgroup\$
    – L. F.
    Commented May 3, 2020 at 3:27
  • \$\begingroup\$ @L.F. It works for my purposes, but do you see anything that could make it closer to bool? \$\endgroup\$
    – S.S. Anne
    Commented May 3, 2020 at 13:03
  • 1
    \$\begingroup\$ If you're doing this for performance reasons, I'd strongly suggest testing. See codereview.stackexchange.com/questions/117880/… for possible inspiration. \$\endgroup\$
    – Edward
    Commented Jan 19, 2022 at 1:36
  • 1
    \$\begingroup\$ I find it funny that this question gets attention almost two years later when I wouldn't write anything akin to this now \$\endgroup\$
    – S.S. Anne
    Commented Jan 19, 2022 at 2:27
  • 1
    \$\begingroup\$ Just use Boost's vector. It intentionally does not have the bool specialization. The common work-around is to use std::deque instead. \$\endgroup\$
    – JDługosz
    Commented Jan 19, 2022 at 20:03

2 Answers 2

4
\$\begingroup\$

If you aren’t reinventing the wheel as a learning exercise, and want to code this productively, the simplest options are to use the Boost version of Boost::Container::Vector<bool>, which does not specialize to a bitfield, or something like std::vector<unsigned char> or perhaps std::vector<std::byte>, letting a type the same size as bool decay to bool. Other suggestions in the past have included using std::deque or specializing the allocator.

If you’re going to re-implement this vector_safe_bool type encapsulating bool, you probably want to define operator(bool) and vector_safe_bool& operator= (bool), which would let you assign bool values to and from the vector. You might also need to define bitwise operators for expressions such as v1[i] &= !v2[i] to work.

Additionally, there’s no point in passing or returning a type that fits into a register by const reference. It’s more efficient to pass by value. And you should allow this type to be constexpr, so you can use use it in the initializer-list of a constexpr std::vector<vector_safe_bool> in C++20.

\$\endgroup\$
2
  • \$\begingroup\$ std::vector is actually constexpr in C++20. \$\endgroup\$
    – user673679
    Commented Jan 18, 2022 at 19:47
  • \$\begingroup\$ @user673679 So it is. Thanks! \$\endgroup\$
    – Davislor
    Commented Jan 18, 2022 at 19:51
3
\$\begingroup\$

Makefile review:

All makefiles should have .DELETE_ON_ERROR: to ensure erroneous output doesn't satisfy dependencies.

The targets all, check and test should be declared as dependencies of .PHONY.

It's unusual to have a Makefile for a header-only library, but I think we can still make use of default rules for the two tests:

clean:
    $(RM) vsb_test

check test:
    $(MAKE) -B CXXFLAGS+=-UUSE_VECTOR_SAFE_BOOL vsb_test clean
    $(MAKE) -B CXXFLAGS+=-DUSE_VECTOR_SAFE_BOOL vsb_test clean

Probably also a good idea to have the compiler auto-generate header dependencies:

CXXFLAGS += -MMD
-include vsb_test.d
\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.