Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Work around for vector<bool>, use basic_string<bool>?

Tags:

c++

Is this a safe workaround? I want to use vector bool but need to pass a pointer to old code expecting C-style array.

typedef std::basic_string<bool> vector_bool;

int main()
{
    vector_bool ab;
    ab.push_back(true);
    ab.push_back(true);
    ab.push_back(true);
    ab.push_back(false);
    bool *b = &ab[0];
    b[1] = false;
}

Edit: Thanks for suggestions of other solutions, but I would really like a definite answer on my above solution. Thanks.

like image 802
Neil Kirk Avatar asked Mar 07 '13 14:03

Neil Kirk


2 Answers

I'm not sure about std::basic_string<bool> because that will instantiate std::char_traits<bool> and I'm not sure if the standard requires that to be defined, or if the char_traits primary template can be left undefined, with only explicit specializations such as char_traits<char> being defined. You're not allowed to provide your own specialization of char_traits<bool> because you can only specialize standard templates if the specialization depends on a user-defined type, which bool obviously isn't. That said, it might work if your stdlib does have a default char_traits definition, and you don't try to use an string operations that require members of char_traits to do anything useful.

Alternatively, this is hacky but might work:

struct boolish { bool value; };
inline boolish make_boolish(bool b) { boolish bish = { b }; return bish; }

std::vector<boolish> b;
b.push_back( make_boolish(true) );
bool* ptr = &b.front().value;

boolish is a trivial type, so as long as an array of boolish has the same representation as an array of bool (which you'd need to check for your compiler, I used a static_assert to check there is no padding) then you might get away with it, although it probably violates the aliasing rules because *ptr and *++ptr are not part of the same array, so incrementing the pointer doesn't point to the next boolish::value it points "past the end" of the previous one (even if those two locations actually have the same address, although [basic.compound]/3 does seem to say that ++ptr does "point to" the next bool).

The syntax gets a bit easier with C++11, you don't need make_boolish ...

#include <vector>
#include <assert.h>

struct boolish { bool value; };

int main()
{
  std::vector<boolish> vec(10);
  vec.push_back( boolish{true} );
  bool* ptr = &vec.front().value;
  assert( ptr[10] == true );
  ptr[3] = true;
  assert( vec[3].value == true );

  static_assert( sizeof(boolish) == sizeof(bool), "" );
  boolish test[10];
  static_assert( sizeof(test) == (sizeof(bool)*10), "" );
}
like image 149
Jonathan Wakely Avatar answered Nov 11 '22 19:11

Jonathan Wakely


From "Working Draft C++, 2012-11-02"

21.1 General [strings.general]
1 This Clause describes components for manipulating sequences of any non-array POD (3.9) type.

21.4.1 basic_string general requirements [string.require]
5 The char-like objects in a basic_string object shall be stored contiguously. That is, for any basic_string object s, the identity &*(s.begin() + n) == &*s.begin() + n shall hold for all values of n such that 0 <= n < s.size().

but

6 References, pointers, and iterators referring to the elements of a basic_string sequence may be invalidated by the following uses of that basic_string object:
— as an argument to any standard library function taking a reference to non-const basic_string as an argument.233
— Calling non-const member functions, except operator[], at, front, back, begin, rbegin, end, and rend.

So, you should be safe as long as you pay attention, not to call these functions, while you use the raw array somewhere else.

Update:

Character traits and requirements are described in 21.2 Character traits [char.traits] and 21.2.1 Character traits requirements [char.traits.require]. Additionally, typedefs and specializations are described in 21.2.2 traits typedefs [char.traits.typedefs] and 21.2.3 char_traits specializations [char.traits.specializations] respectively.

These traits are used in the Input/output library as well. So there are requirements, like eof() or pos_type and off_type, which don't make sense in the context of basic_string.

I don't see any requirement for these traits to be actually defined by an implementatin, besides the four specializations for char, char16_t, char32_t and wchar_t.

Although, it worked out of the box with gcc 4.7 with your example, I defined a minimal bool_traits with just

struct bool_traits {
    typedef bool char_type;
    static void assign(char_type &r, char_type d);
    static char_type *copy(char_type *s, const char_type *p, std::size_t n);
    static char_type *move(char_type *s, const char_type *p, std::size_t n);
};

took the default implementation provided (gcc 4.7), and used that like

std::basic_string<bool, bool_traits> ab;

Your environment might already provide a working implementation. If not, you can implement a simple bool_traits or a template specialization std::char_traits<bool> yourself.

You can see the complete interface for character traits in the Working Draft, PDF or at cppreference.com - std::char_traits.

like image 35
Olaf Dietsche Avatar answered Nov 11 '22 20:11

Olaf Dietsche