Bug in C++ /CLI 2005

  • Thread starter Thread starter Howard Swope
  • Start date Start date
Mark Salsbery said:
This is C++/CLI, not ISO standard C++.

I know, but C++/CLI was supposed to be an attempt to map the CLI as closely
as possible to C++. Having objects come alive without a constructor call is
a serious violation of that ideal.

It also means that the BCL serializers are doing things that can't be
duplicated by user libraries (I previously thought serialization was just
using reflection a bunch and maybe dynamically emitting methods with
lightweight codegen to reduce the overhead of repeated calls, something you
or I could do if we wanted). That's also against C++ thinking.
 
So deserialization creates dead objects that aren't safe to use.
That even SOUNDS wrong!

That means I use a LOT of unsafe dead objects in my apps (I use .NET
serialization extensively) :)

I can't find anything in the ECMA standard for C++/CLI that allows an object
instance to be created without a constructor call.

12.2.2 "Instances of ref classes are created using new-expressions
(§15.4.6)." (15.4.6 is gcnew)
F.3.1 "The object's lifetime begins when all construction is successfully
completed. For the purposes of the C++ Standard (§3.8), "the constructor
call has completed" means the originally invoked constructor call.
[Rationale: Even if a target constructor completes, an outer delegating
constructor can still throw an exception, and if so the caller did not get
the object that was requested. The foregoing decision also preserves the
Standard C++ rule that an exception emitted from a constructor means that
the object's lifetime never began. end rationale]"

Clearly C++/CLI intends to use the same rules for object lifetime as ISO
C++. Deserialized objects whose constructor was never called are not
legitimate C++/CLI objects.

I understand that's probably a big "Ouch!" and we should bring this up with
the C++/CLI language team.

Shucks, even an object that is a member of another object (that's the "stack
semantic members" we're talking about) seems to violate 12.2.2 even though
the constructor is properly called.

Then we have section 13.3 on initialization
"Direct initialization in the C++ Standard (§8.5) occurs in new expressions,
static_cast expressions,
functional notation type conversions, and base and member initializers."

And here in section 19.12 we find something probably related to this
deserialization bug:
"The type of an initonly field shall not be a ref class."
 
Mark said:
That means I use a LOT of unsafe dead objects in my apps (I use .NET
serialization extensively) :)

Mark:

And then there are the "half-dead" objects that have been Dispose'd but not GC'd.
 
David Wilkinson said:
Mark:

And then there are the "half-dead" objects that have been Dispose'd but
not GC'd.


Well half-dead sounds better than dead LOL.

I really need to look into this closely.


And Ben, I've seen your comments as well...I just haven't quite got to this
yet today.


Mark
 
Ben said:
It also means that the BCL serializers are doing things that can't be
duplicated by user libraries (I previously thought serialization was
just using reflection a bunch and maybe dynamically emitting methods
with lightweight codegen to reduce the overhead of repeated calls,
something you or I could do if we wanted).

No, user serializers can also do that. They've just got to use the
method FormatterServices::GetSafeUninitializedObject.

MSDN:
http://msdn.microsoft.com/en-us/lib...atterservices.getsafeuninitializedobject.aspx

"Because the new instance of the object is initialized to zero and no
constructors are run, the object might not represent a state that is
regarded as valid by that object. GetSafeUninitializedObject should only
be used for deserialization when the user intends to immediately
populate all fields."

Joerg
 
Joerg said:
No, user serializers can also do that. They've just got to use the
method FormatterServices::GetSafeUninitializedObject.

MSDN:
http://msdn.microsoft.com/en-us/lib...atterservices.getsafeuninitializedobject.aspx

"Because the new instance of the object is initialized to zero and no
constructors are run, the object might not represent a state that is
regarded as valid by that object. GetSafeUninitializedObject should
only be used for deserialization when the user intends to immediately
populate all fields."

Ugly, but understandable. There's another method on that class,
PopulateObjectMembers, does that allow you to set "initonly" fields of that
uninitialized object?

Can you override serialization so that placement new is called on the object
in between GetSafeUninitializedObject and PopulateObjectMembers, so that the
required constructor call is made? No, because I seem to recall there is no
placement gcnew. Maybe there's another way to explicitly call a
constructor.
 
Ben said:
Ugly, but understandable. There's another method on that class,
PopulateObjectMembers, does that allow you to set "initonly" fields of that
uninitialized object?
Yes, indeed it does.
Can you override serialization so that placement new is called on the object
in between GetSafeUninitializedObject and PopulateObjectMembers, so that the
required constructor call is made? No, because I seem to recall there is no
placement gcnew. Maybe there's another way to explicitly call a
constructor.
Not as far as I am aware. The usual strategy would be to implement
ISerializable if a constructor call is needed.

Joerg
 
Back
Top