Re: Marshalling auto_ptr/unique_ptr objects
On Mar 7, 8:49 am, Daniel Kr?gler <daniel.krueg...@googlemail.com>
wrote:
On 7 Mrz., 09:29, Brian <c...@mailvault.com> wrote:
I'm adding support for auto_ptr/unique_ptr to the C++
Middleware Writer. In the past I've gotten a few pointers
on implementations here and since I'm not an expert on
either of those classes, I would like to get some
feedback on what I have so far. There are two pieces
of input:
Disclaimer: I don't know the C++ Middleware Writer
I'm going to abbreviate that as C++MW below.
and given the context of this newsgroup you
might get further replies that concentrate on the
C++ language aspects of your question. Many
type definitions remain unresolved in your
posting and I didn't look into the details
that your link will probably provide.
I'm only aware of two. msg_length_max and
msg_id_direct. If I had been more careful I'd
have removed this line:
buf->Receive(&msg_id_direct, sizeof(msg_id_direct));
from the Send function in my original post.
What I did was cut some things out of the input
and some, but not all, of the corresponding things
in the output. I should have modified the input and
then regenerated the output, but was being kind of
lazy. Sorry.
class mytype
{
[..]
void
Send(SendCompressedBuffer* buf, bool = false) const
{
SendMemberData(buf);
}
What is the meaning of the bool parameter, that is
not used: Forward compatibility for an intended
future use?
No. It is used to indicate whether the generated
type numbers should be embedded into the stream.
It is an attempt to optimize/minimize the amount
of data that is sent/received. If we have:
class Base {}
class Derived : public Base {}
and want to marshall a vector<Base*>, the boolean
would be true so the type numbers could be included.
If we were marshalling a vector<Derived*>, though,
there isn't any need to embed the type nums. (This
assumes the C++MW knows there aren't any classes
derived from Derived.)
void CalculateMarshallingSize(Counter& cntr) const;
};
And the Middle file is:
tst_shepherd
(auto_ptr<vector<mytype> >, auto_ptr<uint32_t>, auto_ptr<mytype>)
}
Is this a constructor call? I don't understand the
syntax, but I also couldn't find a matching c'tor
in your definition of tst_shepherd given here:
No, it's a form of input to the C++MW. The name given,
tst_shepherd, is used as the name of the generated class.
The next line results in two functions -- a Send and
Receive function being written. The } indicates we're
done with the tst_shepherd middle code.
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)
{
[snipping away everything that is not resolved by your former
definitions]
}
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>;
This looks like a potential memory leak, because I don't
see any guarantee that the following code will not
throw an exception. Why do you not store the pointer into
either of an std::auto_ptr or std::unique_ptr?
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);
This saving is too late. If you would have stored raw1 in
a previous std::auto_ptr raw1, you could replace this by
abt1.reset(raw1.release());
uint32_t* raw3 = new uint32_t;
Same exception-safety problem. Store the memory
*immediately* in a smart pointer
buf->Give(*raw3);
abt2.reset(raw3);
mytype* raw4 = new mytype(buf);
abt3.reset(raw4);
This looks OK.
Good point. I didn't think about that.
Here's my latest shot at it:
// Generated by the C++ Middleware Writer version 1.11
#include <memory>
#include <vector>
#include <File.hh>
#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(&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];
auto_ptr<vector<mytype> > P1(new vector<mytype>);
buf->Give(headCount[0]);
P1->reserve(P1->size() + headCount[0]);
while (headCount[0] > 0) {
--headCount[0];
mytype rep3(buf);
P1->push_back(rep3);
}
abt1.reset(P1.release());
auto_ptr<uint32_t > P3(new uint32_t);
buf->Give(*P3);
abt2.reset(P3.release());
abt3.reset(new mytype(buf));
}
};
uint16_t const mytype_num = 7001;
template <typename B>
inline
mytype::mytype(B* buf)
{
buf->Give(c_);
auto_ptr<uint32_t > P4(new uint32_t);
buf->Give(*P4);
a_.reset(P4.release());
}
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));
}
-----------------------------------------------------
If you want to
dig into the details, there's an archive here --http://webEbenezer.net/build_integration.html--
that has everything needed to compile the above
code. The implementation for unique_ptr is the
same, except it pulls in the file "utility" rather than
"memory."
You need to include header <memory> for std::unique_ptr
as well. AFAIK this was so from the initial proposal
of std::unique_ptr on, and it this is still true for
the recent working draft.
That's kind of a pain. Are there other standard components
that require more than one include? I'm not aware of any
and the Boost/Loki types I support don't need multiple
includes either.
Brian Wood
http://webEbenezer.net
(651) 251-9384
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]