Marshalling auto_ptr/unique_ptr objects

Started by
6 comments, last by wood_brian 14 years, 1 month ago
I'm adding support for auto_ptr/unique_ptr to the C++ Middleware Writer. In the past I've gotten a few pointers on marshalling implementations here and since I'm not an expert on either of those classes, I would like to get a little feedback on what I have so far. There are two pieces of input:

class mytype
{
  std::complex<float>      c_;
  std::auto_ptr<uint32_t>  a_;

  void  SendTypeNum(SendCompressedBuffer*) const;
  void  SendMemberData(SendCompressedBuffer*) const;

public:
  template <typename B>
  explicit mytype(B* buf);

  void
  Send(SendCompressedBuffer* buf, bool = false) const
  {}

  void  CalculateMarshallingSize(Counter& cntr) const;
};
And the Middle file is:

tst_shepherd
  (auto_ptr<vector<mytype> >, auto_ptr<uint32_t>, auto_ptr<mytype>)
}
The output is:

// Generated by the C++ Middleware Writer version 1.11

#include <memory>
#include <vector>
#include <tst.hh>
#include <MarshallingFunctions.hh>
#include <ReceiveCompressedBuffer.hh>
#include <SendCompressedBuffer.hh>


extern uint32_t msg_length_max;

struct tst_shepherd
{

void
Send(SendCompressedBuffer* buf, const auto_ptr<vector<mytype> >& abt1, const auto_ptr<uint32_t>& abt2, const auto_ptr<mytype>& abt3)
{
  Counter cntr(msg_length_max);
  cntr.Add(sizeof(uint32_t));
  groupCount(cntr, false, sizeof(uint16_t), *abt1.get());
  cntr.Add(sizeof(uint32_t));
  abt3->CalculateMarshallingSize(cntr);
  buf->Receive(&msg_id_direct, sizeof(msg_id_direct));
  buf->Receive(&cntr.value_, sizeof(cntr.value_));

  groupSend(buf, false, *abt1.get());

  buf->Receive(abt2.get(), sizeof(uint32_t));

  abt3->Send(buf, false);
  buf->Flush();
}


template <typename B>
void
Receive(B* buf, auto_ptr<vector<mytype> >& abt1, auto_ptr<uint32_t>& abt2, auto_ptr<mytype>& abt3)
{
  uint32_t headCount[1];
  vector<mytype>* raw1 = new vector<mytype>;
  buf->Give(headCount[0]);
  raw1->reserve(raw1->size() + headCount[0]);
  while (headCount[0] > 0) {
    --headCount[0];
    mytype rep3(buf);
    raw1->push_back(rep3);
  }
  abt1.reset(raw1);

  uint32_t* raw3 = new uint32_t;
  buf->Give(*raw3);
  abt2.reset(raw3);

  mytype* raw4 = new mytype(buf);
  abt3.reset(raw4);
}

};

uint16_t const mytype_num = 7001;


template <typename B>
inline
mytype::mytype(B* buf)
{
  buf->Give(c_);
  uint32_t* raw5 = new uint32_t;
  buf->Give(*raw5);
  a_.reset(raw5);
}


inline void
mytype::CalculateMarshallingSize(Counter& cntr) const
{
  cntr.Add(sizeof(c_));
  cntr.Add(sizeof(uint32_t));
}


inline void
mytype::SendTypeNum(SendCompressedBuffer* buf) const
{
  buf->Receive(&mytype_num, sizeof(mytype_num));
}


inline void
mytype::SendMemberData(SendCompressedBuffer* buf) const
{
  complexSend(buf, c_);
  buf->Receive(a_.get(), sizeof(uint32_t));
}

I've only confirmed that the output compiles correctly on g++ 4.4.2. I'd like to hear ideas on what could be done to improve the implementation. In particular, I wonder if the Receive function and mytype's stream constructor are correct and efficient. If you want to dig into the details, there's an archive that has everything needed to compile the above output. Thanks in advance. Brian Wood http://webEbenezer.net (651) 251-9384
Advertisement
Quote:Original post by wood_brian

I'm adding support for auto_ptr/unique_ptr to the C++ Middleware Writer. In the past I've gotten a few pointers on marshalling implementations here and since I'm not an expert on either of those classes, I would like to get a little feedback on what I have so far. There are two pieces of input:

class mytype{  std::complex<float>      c_;  std::auto_ptr<uint32_t>  a_;


Just noticed that that should be unique_ptr since I have a vector of mytype.


Brian Wood
http://webEbenezer.net
(651) 251-9384


Quote:Original post by wood_brian

I'm adding support for auto_ptr/unique_ptr to the C++ Middleware Writer. In the past I've gotten a few pointers on marshalling implementations here and since I'm not an expert on either of those classes, I would like to get a little feedback on what I have so far.


I got some advice on this in this thread.
http://preview.tinyurl.com/ya8tw4f

As I wondered out loud there was some weakness with the initial implementation.

From some limited testing, this support seems to be working. Now I'm thinking about supporting other smart pointers. In particular, I'm wondering about boost::scoped_ptr and boost::weak_ptr. Are you using scoped_ptr in new development or have you decided to use other alternatives? I'm not too familiar with weak_ptr except that it is used in connection with boost::shared_ptr. I'm not planning to add support for shared_ptr at this time, so am not sure if there would be any benefit to adding weak_ptr support. If there are other smart pointers that you're using, I'm interested in hearing about them.


Brian Wood
http://webEbenezer.net
(651) 251-9384
boost::shared_ptr is probably one of the most used smart pointers in some code bases I know. It's basically a poor man's garbage collection system for C++ -- or, alternatively, it's a way that you can get reference counting of any object in a non-intrusive way (unless you choose to derive from enable_shared_from_this).

Weak_ptr ties into shared_ptr, in that a weak_ptr will not keep a shared_ptr object alive, but it will get cleared to NULL when that object dies. Threading users: beware! Every thread of execution into that object needs to own a real shared_ptr to the object.

If I were to choose only one smart pointer from the Boost library, it would be shared_ptr.
enum Bool { True, False, FileNotFound };
How does one go about marshalling pointers anyway? It's very unlikely that address spaces will be the same, so how is a pointer resolved.

With client/server, making all such marhsalled pointers weak, or handles solves some of the problems.

Then there's the C++ nonsense. What if I marshall a pointer to global (singleton problem), pointer to stack-allocated object (doesn't make sense, but perfectly legal), pointer to void * (why not)?

About auto_ptr - how does that work? If object is marhsalled, then original auto_ptr is invalid, but what happens if it gets stuck in network, or is never delivered, or worse - needs to be copied somewhere inside, whereas side-effects of pointee might make that undesirable, perhaps for reasons of exception safety.

Then there's the problem of code ownership. If the code is not replicated (entire pointer tree is followed and replicated), then all calls are remote anyway, but then one needs proxy interfaces and quickly ends up with all that CORBA mess where each thing that can be marshalled needs half a dozen classes and hundreds of IDL-generated support classes. Let alone dealing with migrating instances, where one node creates an instance, then the instance moves to another node, and original one de-allocates and forgets about it.

A simple example:
some_ptr<Foo> foo;receive(foo);foo->????; // what happens here?


It just quickly becomes a quick mess.
Some pointers are obvious -- you'd probably say that a char const * is a C style string, for example.
For other pointers, you have to make up your mind on what to do with them. For example, if the object pointed at is a separately replicable object, then you can marshal it as object ID.
Meanwhile, if the object pointed at is generally marshallable, you probably want to marshal it with identity -- if there are two references to the same object in the same marshal operation, those two objects get the same identity.
Finally, if the pointer is not to a marshalable type, and is not to a replicable object, then you either want to ignore it, or throw an error (ideally a compile-time error) because the user has done something wrong in the IDL at that point.
enum Bool { True, False, FileNotFound };
Quote:Original post by hplus0603
Some pointers are obvious -- you'd probably say that a char const * is a C style string, for example.
For other pointers, you have to make up your mind on what to do with them. For example, if the object pointed at is a separately replicable object, then you can marshal it as object ID.


What do you mean by marshal it as object ID?

Quote:
Meanwhile, if the object pointed at is generally marshallable, you probably want to marshal it with identity -- if there are two references to the same object in the same marshal operation, those two objects get the same identity.


I don't find that comes up very often.


Brian Wood
http://webEbenezer.net
(651) 251-9384


This topic is closed to new replies.

Advertisement