Discussion:
cannot pass objects of non-POD type
John Gateley
2007-10-24 18:55:20 UTC
Permalink
Hi.

I'm having trouble with the dreaded:
cannot pass objects of non-POD type 'sometype' through '...'
message. Here's a brief example:

class String {
public:
void SetData(char *NewData) { m_Data = NewData; }
char *m_Data;
};

int Bar(char *s, va_list ArgList)
{
printf("String: %s\n", s);
printf("Arg2: %s\n", va_arg(ArgList, char *));
}

int Foo(char *s, ...)
{
va_list ArgList;
va_start(ArgList, s);
int Result = Bar(s, ArgList);
va_end(ArgList);
return Result;
}

int main(int argc, char **argv)
{
String MyString;
MyString.SetData("ghi");
Foo("abc", MyString);
}

This works because String::m_Data is public. If I only change
it so that m_Data is private:

class String {
public:
void SetData(char *NewData) { m_Data = NewData; }
private:
char *m_Data;
};

The program now gives the dreaded warning, and crashes on execution.

Given that I know what I am doing (I am using the fact that String
has a single data member which is a pointer to character to copy just
that pointer to character to the argument list), is there any way to
accomplish this?

Is the crash a "real" crash, or is it a crash generated by the compiler
just to enforce passing non-PODness? It definitely seems like the latter.

The situation is this: I have a lot of code that uses a string class
which takes advantage of the pun: the string class has only the
one data member, which means you can do things like printf("%s", obj)
and have the right thing happen (using a different compiler, of course).
Is there any way to use this useful pun with g++?

Thanks,

j
--
John Gateley <***@jriver.com>
Andrew Pinski
2007-10-24 19:03:38 UTC
Permalink
Post by John Gateley
The situation is this: I have a lot of code that uses a string class
which takes advantage of the pun: the string class has only the
one data member, which means you can do things like printf("%s", obj)
and have the right thing happen (using a different compiler, of course).
Is there any way to use this useful pun with g++?
This is way undefined code really. The class is a non POD since you
have a private member. And the C++ standard says it is undefined what
happens when you pass a non-POD for a varable arguments function (the
reasoning is due to virtual functions and knowning the full size of
the struct).

Also using %s with anything but a char* is undefined behavior anyways
(even if it is a struct that only contains a char* since the ABI could
say they are passed differently).

-- Pinski
John Gateley
2007-10-24 19:13:11 UTC
Permalink
On Wed, 24 Oct 2007 12:03:38 -0700
Post by Andrew Pinski
Post by John Gateley
The situation is this: I have a lot of code that uses a string class
which takes advantage of the pun: the string class has only the
one data member, which means you can do things like printf("%s", obj)
and have the right thing happen (using a different compiler, of course).
Is there any way to use this useful pun with g++?
This is way undefined code really. The class is a non POD since you
have a private member. And the C++ standard says it is undefined what
happens when you pass a non-POD for a varable arguments function (the
reasoning is due to virtual functions and knowning the full size of
the struct).
Also using %s with anything but a char* is undefined behavior anyways
(even if it is a struct that only contains a char* since the ABI could
say they are passed differently).
I don't think it is undefined code. The class has no virtual functions,
and the variable argument function doesn't need to know the full size
of the struct, since it is not using it as a String object, it is using
it as a char * pointer (which is what gets passed).

I'm not familiar with "ABI". But perhaps this has something to do
with my other question: is the illegal instruction that gets executed
a "real" illegal instruction, caused by g++ doing something different
that I expected with the String object as an argument? Or is the illegal
instruction just a "place marker" that is generated because I passed
a non-POD as an argument?

Thanks for your reply...

j
--
John Gateley <***@jriver.com>
Andrew Pinski
2007-10-24 19:15:03 UTC
Permalink
Post by John Gateley
I don't think it is undefined code. The class has no virtual functions,
and the variable argument function doesn't need to know the full size
of the struct, since it is not using it as a String object, it is using
it as a char * pointer (which is what gets passed).
Does not matter if the class has no virtual functions or not. The
class is a non POD.
Post by John Gateley
I'm not familiar with "ABI". But perhaps this has something to do
with my other question: is the illegal instruction that gets executed
a "real" illegal instruction, caused by g++ doing something different
that I expected with the String object as an argument? Or is the illegal
instruction just a "place marker" that is generated because I passed
a non-POD as an argument?
The latter. Again this is a non-POD. Does not matter if the class
has no virtual functions or not, it is still undefined code.

-- Pinski
Joe Buck
2007-10-24 19:19:13 UTC
Permalink
Post by Andrew Pinski
Post by John Gateley
I don't think it is undefined code. The class has no virtual functions,
and the variable argument function doesn't need to know the full size
of the struct, since it is not using it as a String object, it is using
it as a char * pointer (which is what gets passed).
Does not matter if the class has no virtual functions or not. The
class is a non POD.
Andrew, I think you're being overly pedantic. While it is true that
the standard permits what I will call "trivial non-PODs" to be laid
out differently from PODs, there's another spec we conform to as well,
the C++ ABI, and that spec specifies in this case that the layout is
exactly the same as a POD.

So GCC could support this case and treat the warning as a pedwarn.
Richard Guenther
2007-10-24 19:37:48 UTC
Permalink
Post by Joe Buck
Post by Andrew Pinski
Post by John Gateley
I don't think it is undefined code. The class has no virtual functions,
and the variable argument function doesn't need to know the full size
of the struct, since it is not using it as a String object, it is using
it as a char * pointer (which is what gets passed).
Does not matter if the class has no virtual functions or not. The
class is a non POD.
Andrew, I think you're being overly pedantic. While it is true that
the standard permits what I will call "trivial non-PODs" to be laid
out differently from PODs, there's another spec we conform to as well,
the C++ ABI, and that spec specifies in this case that the layout is
exactly the same as a POD.
So GCC could support this case and treat the warning as a pedwarn.
How about argument passing conventions?

Richard.
Andrew Pinski
2007-10-24 19:37:50 UTC
Permalink
Post by Joe Buck
So GCC could support this case and treat the warning as a pedwarn.
Well pedwarn is wrong as the code is just undefined at runtime (not at
compile time), pedwarn is for errors when the error is very pedantic.

Also he wants to do printf("%s", struct_containing_charptr); which is
undefined at that point anyways. Even for PODs. He will get a
warning with -Wformat about that being undefined. Yes we follow the
IA64 C++ ABI but we also want people to produce portable code. You
never know some future GCC supports a different C++ ABI which says the
layout is different and then someone goes and tries to do this and
they get the incorrect result. This is the idea of the warning here
really. If you do:

struct b : String
{
virtual void f();
};

struct b c;

void g(char*, ...);

void f(void)
{
g("%s", c);
}

What exactly does that mean? Do we pass it as a String or as a "b"?
This is the reason why non-POD through variable arguments is
undefined.

Thanks,
Andrew Pinski
John Gateley
2007-10-24 21:50:58 UTC
Permalink
On Wed, 24 Oct 2007 12:37:50 -0700
Post by Andrew Pinski
What exactly does that mean? Do we pass it as a String or as a "b"?
This is the reason why non-POD through variable arguments is
undefined.
True, but this relies on "b" being a virtual class.
The case I had was very simple, purposely so.

While for complex objects, passing them could be a disaster,
in this case it is a simple clean construction that is useful.

I'm working on porting approximately a million lines
of code (which also must remain working on the original
platform), and the pun (using a struct/class containing a
single data member which is a pointer to char, and not
containing a vtab) is pervasive throughout. It would be
really nice if I didn't have to do thousands of changes
like:
Format("%s", Object)
becoming
Format("%s", (char *)Object));

Thanks,

j
--
John Gateley <***@jriver.com>
Joe Buck
2007-10-24 22:09:15 UTC
Permalink
Post by John Gateley
On Wed, 24 Oct 2007 12:37:50 -0700
Post by Andrew Pinski
What exactly does that mean? Do we pass it as a String or as a "b"?
This is the reason why non-POD through variable arguments is
undefined.
True, but this relies on "b" being a virtual class.
The case I had was very simple, purposely so.
While for complex objects, passing them could be a disaster,
in this case it is a simple clean construction that is useful.
I'm working on porting approximately a million lines
of code (which also must remain working on the original
platform), and the pun (using a struct/class containing a
single data member which is a pointer to char, and not
containing a vtab) is pervasive throughout. It would be
really nice if I didn't have to do thousands of changes
Format("%s", Object)
becoming
Format("%s", (char *)Object));
An alternative might be to define an overload

void Format(const char* format, const Object& obj) {
Format(format, Object.char_member);
}

and likewise for uses that take more arguments.
Gabriel Dos Reis
2007-10-25 00:52:18 UTC
Permalink
Joe Buck <***@synopsys.COM> writes:

| On Wed, Oct 24, 2007 at 12:15:03PM -0700, Andrew Pinski wrote:
| > On 10/24/07, John Gateley <***@jriver.com> wrote:
| > > I don't think it is undefined code. The class has no virtual functions,
| > > and the variable argument function doesn't need to know the full size
| > > of the struct, since it is not using it as a String object, it is using
| > > it as a char * pointer (which is what gets passed).
| >
| > Does not matter if the class has no virtual functions or not. The
| > class is a non POD.
|
| Andrew, I think you're being overly pedantic. While it is true that
| the standard permits what I will call "trivial non-PODs" to be laid
| out differently from PODs, there's another spec we conform to as well,
| the C++ ABI, and that spec specifies in this case that the layout is
| exactly the same as a POD.
|
| So GCC could support this case and treat the warning as a pedwarn.

I believe there is no question that the code as originally posted
invokes undefined behaviour. C++0x, however, has revised/relaxed the
notion of POD.

-- Gaby
Jack Lloyd
2007-10-24 19:39:06 UTC
Permalink
Post by Andrew Pinski
Post by John Gateley
a "real" illegal instruction, caused by g++ doing something different
that I expected with the String object as an argument? Or is the illegal
instruction just a "place marker" that is generated because I passed
a non-POD as an argument?
The latter.
Is there a reason it's not just an error, then? (As a user) I don't
see the point of something being a warning when the compiled code is
intentionally set up to crash.

-Jack
Andrew Pinski
2007-10-24 20:37:25 UTC
Permalink
Post by Jack Lloyd
Is there a reason it's not just an error, then? (As a user) I don't
see the point of something being a warning when the compiled code is
intentionally set up to crash.
Because the C++ standard (and the C standard) has mentioned that you
cannot diagnostic an undefined runtime behavior. So the code is valid
semantically but undefined at runtime.

Thanks,
Andrew Pinski
Joe Buck
2007-10-24 22:06:42 UTC
Permalink
Post by Andrew Pinski
Post by Jack Lloyd
Is there a reason it's not just an error, then? (As a user) I don't
see the point of something being a warning when the compiled code is
intentionally set up to crash.
Because the C++ standard (and the C standard) has mentioned that you
cannot diagnostic an undefined runtime behavior. So the code is valid
semantically but undefined at runtime.
But the way that the object is passed in this case, and the stack layout,
are completely defined on any platform that obeys the cross-platform API
you will find at

http://www.codesourcery.com/cxx-abi/

and this definition specifies that everything will look exactly the same
as if it were a POD with the same members declared in the same order.
For that reason, while I'm not surprised by the warning message, I
am surprised that the code crashes; it would seem to require extra
work to make it do so.

Not that I would support this particular programming style;
varargs/stdargs gives up type safety and risks runtime errors.
Daniel Jacobowitz
2007-10-25 15:25:52 UTC
Permalink
Post by Joe Buck
Post by Andrew Pinski
Post by Jack Lloyd
Is there a reason it's not just an error, then? (As a user) I don't
see the point of something being a warning when the compiled code is
intentionally set up to crash.
Because the C++ standard (and the C standard) has mentioned that you
cannot diagnostic an undefined runtime behavior. So the code is valid
semantically but undefined at runtime.
But the way that the object is passed in this case, and the stack layout,
are completely defined on any platform that obeys the cross-platform API
you will find at
http://www.codesourcery.com/cxx-abi/
and this definition specifies that everything will look exactly the same
as if it were a POD with the same members declared in the same order.
I don't think that's true. I believe the non-POD must be passed in
memory, but GCC would be permitted to pass the POD in a register if it
preferred. The layout is defined by the C++ ABI, but not the argument
passing conventions.
--
Daniel Jacobowitz
CodeSourcery
Joe Buck
2007-10-25 15:44:26 UTC
Permalink
Post by Daniel Jacobowitz
Post by Joe Buck
But the way that the object is passed in this case, and the stack layout,
are completely defined on any platform that obeys the cross-platform API
you will find at
http://www.codesourcery.com/cxx-abi/
and this definition specifies that everything will look exactly the same
as if it were a POD with the same members declared in the same order.
I don't think that's true. I believe the non-POD must be passed in
memory, but GCC would be permitted to pass the POD in a register if it
preferred. The layout is defined by the C++ ABI, but not the argument
passing conventions.
One of the purposes of the C++ ABI is to allow different compilers to
interoperate. The freedom you describe would prevent gcc-compiled
code from behaving correctly with icc-compiled code, for example.
So yes, argument passing conventions are part of the ABI.

Now, in this case there is an "out": a compiler developer can argue
that passing a non-pod to a variadic function isn't defined, so the
ABI doesn't matter.
Daniel Jacobowitz
2007-10-25 17:11:50 UTC
Permalink
Post by Joe Buck
One of the purposes of the C++ ABI is to allow different compilers to
interoperate. The freedom you describe would prevent gcc-compiled
code from behaving correctly with icc-compiled code, for example.
So yes, argument passing conventions are part of the ABI.
You're using the term "ABI" too broadly - more than one applies. The
C++ ABI only covers C++ specific additions on top of existing
platform-specific ABIs. It's not a question of freedom.

In any case, it seems that I was wrong. Non-PODs still get passed in
registers if the layout-equivalent POD would, unless there is a
non-trivial copy constructor or destructor; there are non-PODs without
those. Sorry for the confusion.
--
Daniel Jacobowitz
CodeSourcery
Loading...