2

The classic 32-bit Borland/Embarcadero compiler - a.k.a. bcc32 - exhibits a strange failure when a traits class is specialised for std::vector<bool>. In particular, it fails to compile usages of the specialisation because it doesn't find any of its members. With other types - like std::vector<char> - there is no problem at all. Tested with BC++ 5.5.1 (free) and BC++ 7.1 (RX/Seattle).

Is there a workaround for this?

#include <iostream>
#include <typeinfo>
#include <vector>

template<typename T>
struct traits {  };

template<> struct traits< std::vector<char> >
{
   enum {  ENUM = 42  };

   static int func ()  {  return ENUM;  }
};

template<> struct traits< std::vector<bool> >
{
   enum {  ENUM = 666  };

   static int func ()  {  return ENUM;  }
};

///////////////////////////////////////////////////////////////////////////////////////////////////

template<typename T>
void test ()
{
   typedef traits<T> TT;

   // separate lines to see exactly where the compiler barfs
   std::cout << typeid(T).name();
   std::cout << " " << TT::ENUM;        // E2451 Undefined symbol 'ENUM'
   std::cout << " " << TT::func();      // E2451 Undefined symbol 'func'

   TT tt;
   std::cout << " " << tt.ENUM;         // E2316 'ENUM' is not a member of 'traits<std::_Bvector>'
   std::cout << " " << tt.func();       // E2316 'func' is not a member of 'traits<std::_Bvector>'
   std::cout << "\n";
}

int main ()
{
   test< std::vector<char> >();
   test< std::vector<bool> >();
   return 0;
}

Note: a somewhat hackish workaround that can be useful in certain circumstances is to code the specialisation for vector<bool> into the primary template (which would normally be left undefined); the specialisations on other types can then be done as usual, and the code works as expected even with bcc32.

A runtime assert can verify that the only unspecialised incarnation of the traits template is the one for std::vector<bool>. Templates that use the traits would then invoke the assertion code in a convenient place (which could also be a static function).

template<typename T>
struct traits
{
   // specialisation for std::vector<bool> coded here...
   enum {  ENUM = 666  };

   static int func ()  {  return ENUM;  }

   static void assert_only_vector_bool_not_specialised ()
   {
      assert(typeid(T) == typeid(std::vector<bool>));
   }
};

struct traits_specialisation_base
{
   static void assert_only_vector_bool_not_specialised ()
   {
   }
};

template<> struct traits< std::vector<char> >: traits_specialisation_base
{
   enum {  ENUM = 42  };

   static int func ()  {  return ENUM;  }
};

// ...

template<typename T>
struct UsingTraits
{
   typedef traits<T> TT;

   UsingTraits ()
   {
      TT::assert_only_vector_bool_not_specialised();
   }
};

// ...

UsingTraits< std::vector<char> > erna;
UsingTraits< std::vector<bool> > fred;
DarthGizka
  • 4,347
  • 1
  • 24
  • 36
  • The simple workaround is to use a compiler from *after* the 1998 standardization of C++. – Cheers and hth. - Alf Jan 24 '16 at 12:00
  • @Che: As mentioned in the first paragraph of my post, version 7.10 of bcc32 - the one released with RAD Studio RX/Seattle **last autumn** - has the exact same problem as the older versions. And no, when using bcc32 then it's not an option to use a standards-compliant compiler. It is what it is. – DarthGizka Jan 24 '16 at 12:11
  • 1
    @Cheersandhth.-Alf Since it is `vector – Ami Tavory Jan 24 '16 at 12:58
  • @Ami: The point of the code I'm developing is to confront the performance (or lack thereof) of `vector` with that of `vector` and raw memory treated as a packed bitmap, by traitsing these types into various prime sieve implementations with different characteristics. Another point is to show the behaviour not only for optimising compilers like gcc and VC++, but also for non-optimising ones like bcc32 and bcc/clang. Hence, both `vector – DarthGizka Jan 24 '16 at 13:13
  • @DarthGizka: is this a problem only in the [**classic** 32bit compiler (bcc32)](http://docwiki.embarcadero.com/RADStudio/en/BCC32), or are the clang-based [64bit (bcc64)](http://docwiki.embarcadero.com/RADStudio/en/BCC64) and [**new** 32bit (bcc32c)](http://docwiki.embarcadero.com/RADStudio/en/BCC32C) compilers also affected? – Remy Lebeau Jan 26 '16 at 06:57
  • @Remy: the problem affects only the 'classic' compilers, not the clang-based ones. – DarthGizka Jan 26 '16 at 11:08

1 Answers1

1

There is something fishy in the std:: with std::vector<bool> so you need to use the std:: type instead. just change to:

#include <iostream>
#include <typeinfo>
#include <vector>
//---------------------------------------------------------------------------
template<typename T> struct traits
    {
    // this is safe constructor/destructor for Borland BDS2006 and later
    traits(){};
    traits(traits& a){};
    ~traits(){};
    traits* operator = (const traits *a){};
    //traits* operator = (const traits &a){}; // use this only if you have dynamic allocation members
    };

template<> struct traits< std::vector<char> >
    {
    enum {  ENUM = 42  };
    static int func ()  {  return ENUM;  }
    };

template<> struct traits< std::_Bvector >   // here use the std type directly
    {
    enum {  ENUM = 666  };
    static int func ()  {  return ENUM;  }
    };
//---------------------------------------------------------------------------
template<typename T> void test ()
    {
    typedef traits<T> TT;

    // separate lines to see exactly where the compiler barfs
    std::cout << typeid(T).name();
    std::cout << " " << TT::ENUM;        // E2451 Undefined symbol 'ENUM'
    std::cout << " " << TT::func();      // E2451 Undefined symbol 'func'

    TT tt;

    std::cout << " " << tt.ENUM;         // E2316 'ENUM' is not a member of 'traits<std::_Bvector>'
    std::cout << " " << tt.func();       // E2316 'func' is not a member of 'traits<std::_Bvector>'
    std::cout << "\n";

    // can ignore this ... it is just output to memo I do not use console
    AnsiString s="";
    s=s+typeid(T).name() + "\n";
    s=s+" " + AnsiString( TT::ENUM ) + "\r\n";        // E2451 Undefined symbol 'ENUM'
    s=s+" " + AnsiString( TT::func() ) + "\r\n";      // E2451 Undefined symbol 'func'
    s=s+" " + AnsiString( tt.ENUM ) + "\r\n";         // E2316 'ENUM' is not a member of 'traits<std::_Bvector>'
    s=s+" " + AnsiString( tt.func() ) + "\r\n";       // E2316 'func' is not a member of 'traits<std::_Bvector>'
    Form1->mm_log->Lines->Add(s);
    }
//---------------------------------------------------------------------------
// this is your main()
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
    {
    test< std::vector<char> >();
    test< std::vector<bool> >();
    }
//---------------------------------------------------------------------------

I use windows form app so ignore form stuff. The constructors/destructors are not necessary for compilation but you should add them because of the Borland BDS2006 and latter C++ engine bug. For more info see:

The code above gives me this output:

std::vector<char,std::allocator<char> >
 42
 42
 42
 42

std::vector<std::allocator<bool> >
 666
 666
 666
 666
Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • 1
    Thanks, the trick with specialising on `std::_Bvector` or `std::allocator` instead of `std::vector` works like a charm (tested with bcc32 5.5.1 and 7.10), and it is much cleaner than my ugly hack! And thanks for working out the details and solution for the destructor problem in the linked articles. Kudos! – DarthGizka Jan 25 '16 at 18:52
  • @DarthGizka glad to be of help – Spektre Jan 25 '16 at 20:35