Sequence - SIGABRT invalid pointer (C++)

7 posts / 0 new
Last post
Offline
Last seen: 5 years 6 months ago
Joined: 01/14/2015
Posts: 8
Sequence - SIGABRT invalid pointer (C++)

Hi,

I'm struggling with an embedded sequence within a struct. When I try to send it, the application terminates with a SIGABRT.

Given the following IDL:

-------------------------snip-----------------------------
const long MAX_STRING_LENGTH = 256;

enum StudentStatus{
ENROLLED,
ALUMNI
};

struct Student{
string<MAX_STRING_LENGTH> name;
string<MAX_STRING_LENGTH> familyname;
StudentStatus status;
};

struct Schools{
string<MAX_STRING_LENGTH> schoolname; //@key
sequence<Student> students;
};
-------------------------snip-----------------------------

I create in my application the following SchoolsSeq. (Participant, Publisher, Writer... have been created properly before, but not shown here)

-------------------------snip-----------------------------
std::string name = "Bart";
std::string familyName = "Simpson";

Student student1;

//make Char[] out of Strings
char tmpName[name.length()+1];
strcpy(tmpName, name.c_str());

char tmpFamilyName[familyName.length()+1];
strcpy(tmpFamilyName, familyName.c_str());

student1.name = tmpName;
student1.familyname = tmpFamilyName;
student1.studentstatus = ALUMNI;

StudentSeq studentSec1;
studentSec1.maximum(5);
studentSec1.length(1);
studentSec1[0] = student1;

School mySchool;

std::string schoolName = "Springfield Elementary";
char tmpSchoolName[schoolName.length()+1];
strcpy(tmpSchoolName, schoolName.name.c_str());

mySchool.schoolname = tmpSchoolName;
mySchool.students = studentSec1;

DDS_ReturnCode_t retcode = DDS_RETCODE_OK;
DDS_InstanceHandle_t handle = DDS_HANDLE_NIL;

retcode = _writer->write(mySchool, handle);
-------------------------snip-----------------------------

The Debugger shows me that the program fails when executing (in the SchoolTest.cxx (generated by rtiddsgen2)):

-------------------------snip-----------------------------
void Student_finalize_w_params(
Student* sample,const struct DDS_TypeDeallocationParams_t * deallocParams)
{

if (sample) {} /* To avoid warnings */
if (deallocParams) {} /* To avoid warnings */

if (sample->name != NULL) {
-----> DDS_String_free(sample->name);
-------------------------snip-----------------------------

The problem must be related to the "name" / "familyname" Strings. When I remove those fields, so that just the ENUM StudentStatus remains in the STRUCT Student, then data get's written as expected onto the network / shared memory.

Could anyone give me a hint what my problem is?

Any help appreciated!

Thanks,
Tobias

Keywords:
rip
rip's picture
Offline
Last seen: 1 day 18 hours ago
Joined: 04/06/2012
Posts: 322

You've set sample->name to a char[] that is allocated in the stack.  The DDS_String_free(...) is looking for a pointer generated by DDS_String_alloc(...).

See https://community.rti.com/rti-doc/510/ndds.5.1.0/doc/html/api_cpp/group__DDSStringClass.html

cf. https://community.rti.com/kb/why-shouldnt-i-use-ddsstringdup-initialize-string-data for some more background info.

Offline
Last seen: 5 years 6 months ago
Joined: 01/14/2015
Posts: 8

Hi rip,

thanks for your reply. You are right - I changed to code:

-------------------------snip-----------------------------
std::string name = "Bart";
std::string familyName = "Simpson";

Student student1;

strcpy(student1.name, name.c_str());
strcpy(student1.familyName, familyName.c_str());
student1.studentstatus = ALUMNI;

-------------------------snip-----------------------------

However, I still get the same error. Looking with the Debugger into it, I found that the pointers to student1.name and student1.familyName are not initialized properly.

adding:

student1.name = DDS_String_alloc(256);
student1.familyName = DDS_String_alloc(256);

solves the problem. But my understand is, that the code, generated by rtiddsgen2 should allocate a char* DDS_String_alloc(size) automatically for the string fields during the creation of the student1 object. right?

Thanks,
Tobias

rip
rip's picture
Offline
Last seen: 1 day 18 hours ago
Joined: 04/06/2012
Posts: 322

If you open an rtiddsgen Foo_publisher.cxx (created by rtiddsgen if you use -example {arch} -language c++, on an IDL file that has a struct Foo { ... }, you can find this chunk:

    /* Create data sample for writing */

    instance = FooTypeSupport::create_data();

    if (instance == NULL) {
        printf("FooTypeSupport::create_data error\n");
        publisher_shutdown(participant);
        return -1;
    }

You'll note that I am not using the constructor, ie no Foo instance = new Foo(); or Foo instance; like you are doing.

In the FooPlugin.cxx file, find the definition of ...::create_data(void):

Foo *
FooPluginSupport_create_data(void)
{
    return FooPluginSupport_create_data_ex(RTI_TRUE);
}

In the FooPluginSupport_create_data_ex(boolean):Foo *, the boolean arg represents the "deeply allocate" flag.  i.e., deeply allocate pointers to things like strings.

I'm pretty sure the problem here is that the C++ generated code is simply a thin veneer over the top of the C libraries (thus FooPluginSupport_create_data_ex(bool)).  So it may be that your (logical) use of a C++ constructor, and (logical) assumption that this calls ..._create_data(true) may be invalid (as you might tell, I am dubious about the merits of C++ as a language). 

What happens if you do this, instead:

Student * student1 = StudentTypeSupport_create_data(); // see the generated <Foo>_publisher.cxx for an example

 

 

 

Offline
Last seen: 5 years 6 months ago
Joined: 01/14/2015
Posts: 8

Bingo!

Student * student1 = StudentTypeSupport::create_data();

did the job. Now the memory is properly allocated.

Thanks for your help and your explanations. They are very helpful for getting a deeper understanding.

Tobias

Gerardo Pardo's picture
Offline
Last seen: 1 month 3 weeks ago
Joined: 06/02/2010
Posts: 594

Yes, the constuctor will not initialize the object.

This is because the C++ class defined for the IDL-defined types does not expliclty define the constructor so the default C++ constructor, which does not initialize the members, is the one used. You can see this by examining the header file that was generated. That is the file called <IDLFileName>.h; Look for "class Student" there and as you can see thre is no constructor defined...

The end-result is that you should create the data using the <TypeSupport>::create_data() operation and not the C++ constructor and delete it with the <TypeSupport>::delete_data()

We did this to keep most of the generated code in the TypeSupport. However we can also see how this creates some potential confusion and we probably need to re-think this aproach and perhaps define the C++ some constructors explicitly...

Gerardo

Offline
Last seen: 5 years 6 months ago
Joined: 01/14/2015
Posts: 8

Hi Gerardo,

honestly, yes. It's a bit confusing. One would expect that the members are initialized on instantiation.

BR Tobias