Using gcc to compile a program that includes support for decimal data types, I recently encountered the following error:
error: type transparent class 'std::decimal::decimal32' has base classes
A quick look at GCC's source tree shows that this error message is found in gcc/cp/class.c
.
What is a "type transparent class"? Why is it an error for such a class to have "base classes"?
Reading the source code of GCC a bit more, in semantics.c
:
if (TREE_CODE (t) == RECORD_TYPE
&& !processing_template_decl)
{
tree ns = TYPE_CONTEXT (t);
if (ns && TREE_CODE (ns) == NAMESPACE_DECL
&& DECL_CONTEXT (ns) == std_node
&& DECL_NAME (ns)
&& !strcmp (IDENTIFIER_POINTER (DECL_NAME (ns)), "decimal"))
{
const char *n = TYPE_NAME_STRING (t);
if ((strcmp (n, "decimal32") == 0)
|| (strcmp (n, "decimal64") == 0)
|| (strcmp (n, "decimal128") == 0))
TYPE_TRANSPARENT_AGGR (t) = 1;
}
}
This code means that a type is marked transparent if:
std::decimal
.decimal32
, decimal64
or decimal128
.In class.c
there is the error check you encountered, and a few more.
And in mangle.c
:
/* According to the C++ ABI, some library classes are passed the
same as the scalar type of their single member and use the same
mangling. */
if (TREE_CODE (type) == RECORD_TYPE && TYPE_TRANSPARENT_AGGR (type))
type = TREE_TYPE (first_field (type));
The comment is key here. I think it means that a transparent type is replaced for the type of its fist (and only) member, so it can be used anywhere it first member can. For example, in my include/decimal
the class std::decimal::decimal32
has a single field of type __decfloat32
(from a previous typedef float __decfloat32 __attribute__((mode(SD)));
), so any function that takes a __decfloat32
can take a std::decimal::decimal32
and vice-versa. Even the function decoration is done the same. The idea is probably to make this classes ABI compatible with the C types _Decimal32
, _Decimal64
and _Decimal128
.
Now, how are you getting a class decimal32
with base classes? My only guess is that you are including incompatible (maybe older) header files, with a totally different implementation.
UPDATE
After some investigation, it looks like my guess about the ABI and function decoration is right. The following code:
#include <decimal/decimal>
using namespace std::decimal;
//This is a synonym of C99 _Decimal32, but that is not directly available in C++
typedef float Decimal32 __attribute__((mode(SD)));
void foo(decimal32 a) {}
void foo(Decimal32 a) {}
gives the curious error:
/tmp/ccr61gna.s: Assembler messages:
/tmp/ccr61gna.s:1291: Error: symbol `_Z3fooDf' is already defined
That is, the compiler front-end sees no problem in the overload and emits the asm code, but since both functions are decorated the same the assembler fails.
Now, is this a non-conformance of GCC, as Ben Voigt suggests in the comments? I don't know... you should be able to write overloaded functions with any two different types you want. But OTOH, it is impossible to get the Decimal32
type without using some compiler extension, so the meaning of this type is implementation defined...
As mentioned in one of my comment, a type-transparent class is a wrapper class to some primitive type, like integers, etc.
They are called transparent because of their use of operator overloading, which makes them act just like the primitive type they wrap.
IE, to wrap an int
transparently in a class, you'll need to overload the =
operator, the ++
operator, etc...
Apparently, GNU's libstdc++ uses such classes for some types. Not sure why...
About the base class issue, while I'm not 100% sure, here's a guess.
When dealing with inheritance in C++, you'll often need to declare virtual methods, to resolve issues with upcasting.
Declaring a method as virtual will tell the compiler to create a virtual table for the methods, so they can be looked at runtime.
This will of course increase the instance size of the class.
For a type-transparent class, this is not acceptable, as the compiler won't be able to place an instance of such a class in a register (i.e when passing arguments, etc), unlike the wrapped type, and so the class won't be transparent anymore.
Edit
I've no idea how to declare such a transparent-class in GCC. The closest thing I can think of is transparent unions:
http://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html
Something like:
class IntWrapper
{
int _x;
/* Constructor, operator overloads... */
};
typedef union
{
int integerValue;
IntWrapper integerWrapper;
}
IntUnion __attribute__( ( __transparent_union__ ) );
My GCC version does not seem to support it, but according to the documentation (see above link), this would allow int
or IntWrapper
to be passed to functions transparently using the same calling convention as int
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With