simple take next sample fails

5 posts / 0 new
Last post
Offline
Last seen: 6 years 1 week ago
Joined: 10/04/2013
Posts: 4
simple take next sample fails

I'm trying to do something that I thought was really simple!   Just this:

DDS::ReturnCode_t code;

DDS::SampleInfo info;

TestTopic sample;

code = dataReader->take_next_sample(sample,info);

 

That always fails with:

PRESPsReaderQueue_readOrTakeNextSample:!copy

PRESPsREader_takeNextSample:!queue takeNextSample

 

What does that mean and what am I doing wrong?

 

Thanks,

Frank

 

Organization:
Gerardo Pardo's picture
Offline
Last seen: 3 weeks 14 hours ago
Joined: 06/02/2010
Posts: 589

Can you share the IDL for TestTopic?

Most likely what is happening is that TestTopic is not being properly initialized. This would be the case if it internally contained strings, sequences, and such.

This happens because the code that rtiddsgen generates does not define a constructor for TestTopic that initializes it, so you need to either create TestTopic using the TestTopicSupport as in:

    TestTopic *data = TestTopicSupport::create_data();

Or alternatively initialize it explicitly after constructing in:

    TestTopic data;
    TestTopic_initialize(&data);

Gerardo

Offline
Last seen: 6 years 1 week ago
Joined: 10/04/2013
Posts: 4

Hi Gerardo,

Thanks for the quick response.   That was the problem alright.   I don't think that you'd really be that interested in the IDL... just a struct with 3 fields.   I can show you if you'd really like, but I'd rather ask some other follow-up questions.   I had seen that create_data() thing before, but was hoping that it wasn't actually necessary.   The reason being is that I'm writing a class that returns a pointer to that topic data, i.e., sharing the instance created by "create_data()", after the take with other objects.

Is it legal to have the receiving object simply "delete" the instance when it is done?   I'm guessing that if you don't have a constructor, then you don't have a destructor either!  If I cannot do that, I really don't want the calling object to have to know about T::TypeSupport but will have to do something like use a shared_ptr with a custom deleter (that calls the T::TypeSupport::delete_data() for it.  That way calling objects can remain ignorant of everything but the topic data type.

I'm guessing that the above scenario is fairly common.   Does RTI have any associated HOWTO or best practice for that sort of thing?

Thanks,

Frank

 

 

Gerardo Pardo's picture
Offline
Last seen: 3 weeks 14 hours ago
Joined: 06/02/2010
Posts: 589

Hi Frank,

You are not the first to be confused with this. I know we are considering adding the constructor and destructor code to the code generator. Not sure if that has been decided/scheduled but I will take a look into it. You could always edit/post process the code we generate if you wanted to do it yourself, but this is painlful at best... However if you want the calling objects to really be ignorant of the TypeSupport and be able to delete the objects I think editing the generated code to add these functions may be your best option.

Yes. We do not generate explicit constructors or destructors. So the ones you get are the ones the compiler generates which are not smart about internally-allocated memory. For this reason you must call the TestTopic_initialize() and TestTopic_finalize() explicitly.

Regarding whether the receiver can "delete" the object when done. The answer depends on what API call you use.

The read/take operations that take a single referene to the  TestTopic, things like take_next_sample() expect the TestData to be managed by the caller. You create it, initialize it, pass it to the read/take which fill the data. The caller owns that data so it can do as it pleases. If you want to delelete it after you are done you are free to do so. However see the 'best practice' comment at the end.

The read/take operations that take a sequence of TestTopic data (i.e. a TestTopicSeq)  expect the sequence to be initialized as empty and return the sequene pointing to TestData elements that are internally managed by the middleware. These you cannot delete. You need to call DataReader::return_loan() to tell the middleware you have finished with the TestTopicSeq and the middleware takes care of them.

That said, in general it is not good practice to allocate/delete data each time you receive something because memory allocation can be slow and non-deterministic and worse it can fragment your memory so your performance may degrade over time. The best practice is to either use the read/take that takes sequences so that the middleware can manage that memory or else if you do not use the sequence operations, then manage the TestTopic objects yourself, but recycle the TestTopic objects so you are not allocating/freeing all the time.

Gerardo

 

 

Offline
Last seen: 6 years 1 week ago
Joined: 10/04/2013
Posts: 4

Well, ok.  I still think that I'll look into the shared_ptr with custom deleter thing.   I didn't want to complicate this thread before, but I'm actually writing a generic (templated) class that works with any IDL-generated topic.   I  have a class that does just about everything under the hood for novice users.

MyClass<IDLTopic> obj(domainId,topicName,qosProfile);

IDLTopic* sample;
sample = obj.read();	// this method's implementation is what I'm talking about now
obj >> sample;		// can get a pointer to the next sample this way too

// after processing, my calling objects have to know about type support to do this:
IDLTopic::TypeSupport::delete_data(sample);

// which is kind of a bummer!  :-)
          

 So, just returning a (typename T*) from the read() will probably be insufficient unless I provide a method to delete the topic data as well.   That is why I think that the boost::share_ptr is the way to go.   I can make a custom deleter like:

template <typename T>
class MyDeleter {
    public:
	void operator() (T* p) {
	    T::TypeSupport::delete_data(p);
	}
};
       

...or something very similar and then have my read return a boost::shared_ptr<T*> instead.   Of course that will likely complicate my stream input operator, but oh well, memory management is the primary concern here.

 

Thanks,

Frank