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;
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