Click to See Complete Forum and Search --> : struct troubles...


tanis11
May 28th, 2008, 06:10 PM
Basically, I'm trying to get back a few structs from the results of a few functions called within a function in order to pass those values into something else later. However, I'm not having any luck.

I took out bits of my code for an example and reduced it down to one struct (however, I don't want to use it as the "return value"... as in my whole app I need a single function to return a bunch of structs by pushing them in as parameters that may or may not hold values).

For "simpleTest"... things work as I'd expect. For my "linuxTest"... the results are odd for my struct. I'm missing something stupid... I know it. (For those not familiar with Linux and the Linux function I'm calling... I'm hoping it might be obvious enough just looking at it... maybe... not sure.)

Thanks for any help you can give me!


#include <stdio.h>
#include <pwd.h>

struct testStruct
{
int x;
char *y;
};

void linuxTest( struct passwd *pwdResult )
{
struct passwd pwd;
char pwdBuffer[ 4096 ];
uid_t myUid = 501;
char* uname;
if ( getpwuid_r( myUid, &pwd, pwdBuffer, 4096, &pwdResult ) == 0 &&
pwdResult != NULL )
{
uname = pwdResult->pw_name;
}
else
{
uname = "unknown";
}
printf( "Username: %s\n", uname );
}

void linuxTest()
{
struct passwd pwdResult;
linuxTest( &pwdResult );
printf( "Username: %s\n", pwdResult.pw_name );
}

void simpleTest( struct testStruct *test )
{
test->x = 7;
test->y = "some value";
printf( "TestValue: %s\n", test->y );
}

void simpleTest()
{
struct testStruct test;
simpleTest( &test );
printf( "TestValue: %s\n", test.y );
}

int main()
{
printf( "Starting...\n" );
simpleTest();
linuxTest();
printf( "End.\n" );
return( 0 );
}


RESULTS:

Starting...
TestValue: some value
TestValue: some value
Username: dougs
Username: X�����
End.

TheCPUWizard
May 28th, 2008, 06:29 PM
1) You are attempting to pass temporaries.

2) You do not need to explicitly use "struct" anywhere except the declaration. This is C++, not C!

3) Do NOT use Raw Pointers, their only purpose is to induce bugs into your program. Use the appropriate STL (or other library) containers. Pay specific attention to std::string and std::vector.

souldog
May 28th, 2008, 07:31 PM
to be more precise,



void linuxTest( struct passwd *pwdResult )
{
struct passwd pwd;
char pwdBuffer[ 4096 ]; // THIS IS THE MEMORY FOR THE INFORMATION PUT IN YOUR passwd structure
uid_t myUid = 501;
char* uname;
if ( getpwuid_r( myUid, &pwd, pwdBuffer, 4096, &pwdResult ) == 0 &&
pwdResult != NULL )
{
uname = pwdResult->pw_name;
}
else
{
uname = "unknown";
}
printf( "Username: %s\n", uname );
}

void linuxTest()
{
struct passwd pwdResult;
linuxTest( &pwdResult );
//THE MEMORY HAS GONE OUT OF SCOPE. pwdResult CONTAINS INVALID POINTERS
printf( "Username: %s\n", pwdResult.pw_name );
}

tanis11
May 29th, 2008, 12:53 AM
Thank your VERY much for the feedback. It is very much appreciated. Being in Java 99% of the time... as you can tell... C++ can throw me a bit. I will work on making it more C++ friendly for sure.

So, would it be possible to pass in an empty struct and then to clone it with the results of the local struct. Something like this:



void linuxTest( passwd *infoToRetain )
{
passwd pwd;
char pwdBuffer[ 4096 ];
passwd *pwdResult
uid_t myUid = 501;
char* uname;
if ( getpwuid_r( myUid, &pwd, pwdBuffer, 4096, &pwdResult ) == 0 &&
pwdResult != NULL )
{
uname = pwdResult->pw_name;
infoToRetain = pwdResult; // Does NOT work
// // The following appears to work for the one value if uncommented
// infoToRetain->pw_name = pwdResult->pw_name;
// // So... would I have to copy each member in the passwd struct ... or is there a better way?
}
else
{
uname = "unknown";
}
printf( "Username: %s\n", uname );
}

void linuxTest()
{
passwd infoToRetain;
linuxTest( &infoToRetain );
printf( "Username: %s\n", infoToRetain.pw_name );
}


Thanks again for the help.

laserlight
May 29th, 2008, 01:48 AM
It does not work as you are assigning one pointer to another, but pointers are passed by value. Think of your understanding of Java references, since Java references are like pointers without the pointer syntax.

The simplest fix, if this is really C++ code, is to pass the pointer by reference:
void linuxTest( passwd*& infoToRetain )

souldog
May 29th, 2008, 02:11 AM
the easiest fix for this is


struct PasswordInfo
{
passwd Pwd_;
char Buffer_[4096];
bool Valid_;
}

void linuxTest(PasswordInfo& infoToRetain )
{
passwd* pwdResult(0);
uid_t myUid(501);
if(getpwuid_r( myUid, &infoToRetain.Pwd_, infoToRetain.Buffer_, 4096, &pwdResult ) == 0 && pwdResult != NULL)
infoToRetain.Valid_ = true;
else
infoToRetain.Valid_ = true;
}

void linuxTest()
{
PasswordInfo infoToRetain;
linuxTest(infoToRetain);
if(infoToRetain.Valid_)
printf( "Username: %s\n", infoToRetain.Pwd_.pw_name );
else
...
}


this is not the best way

tanis11
May 29th, 2008, 02:42 AM
In all honesty, I am floored by what an incredible site this is. Two quick and easy to digest replies within an hour... a serious thank you for the efforts for this issue.

I can see how wrapping the struct in the struct would work. Makes sense to me. I think I've got my head also around how sending a reference to my pointer would work. I put together another quick sample to see if I've got that right.

To take the struct out of the picture and to just use the two values from the returned struct I'm really interested in keeping around... does the following make sense... or am I way off again?


#include <iostream>
#include <pwd.h>

void linuxTest( uid_t& lastUid, char*& lastUsername )
{
passwd *pwdResult;
passwd pwd;
char pwdBuffer[ 4096 ];
uid_t myUid = 501;
if ( getpwuid_r( myUid, &pwd, pwdBuffer, 4096, &pwdResult ) == 0 &&
pwdResult != NULL )
{
lastUid = pwdResult->pw_uid; // Is this safe?
lastUsername = pwdResult->pw_name; // And this? This referenced pointer is now pointing to a local pointer. Will that data this local pointer is pointing to be around when it comes back as I now have another pointer looking at it?
}
}

void linuxTest()
{
uid_t lastUid = 0;
char * lastUsername = "root";
linuxTest( lastUid, lastUsername );
std::cout << "Results for linuxTest - lastUsername: " << lastUsername << std::endl;
}

void simpleTest( int& lastUid, char*& lastName )
{
int localUid = 8;
lastUid = localUid;
char* localName = "dougs";
lastName = localName; // OK to do? lastName pointed to an address that is gone for good. Pointing it to this local pointer would seem to be a problem because it is due to be killed when I leave this function... but am I safe to assume the value will not be killed because my referenced pointer passed in is now fixed to it?
}

void simpleTest()
{
int lastUid = 0;
char* lastName = 0;
simpleTest( lastUid, lastName );
std::cout << "Results for simpleTest - lastName: " << lastNamestd << std::endl;
}

int main()
{
simpleTest();
linuxTest();
return( 0 );
}

souldog
May 29th, 2008, 03:10 AM
I have never actually used this function, but my read of the documentation, which says

buffer
Points to a buffer where the data needed for the passwd structure members is to be placed.


means that the pointers which are in the passwd structure will be pointing into the buffer you provide with

char pwdBuffer[ 4096 ];

so you can not just assign this pointer to one you pass into your function since the buffer no longer exists after the function exits.

That is why I included the buffer in the structure passed into the function
in my version. This ensures that the buffer where the data actually is placed has lifetime as long as you have the PasswordInfo struct.


Now assuming that the char* members of the passwd point to null terminated
strings, you should use a std::string to get the char* data



void linuxTest(uid_t& lastUid, std::string& lastUsername )
{
passwd *pwdResult;
passwd pwd;
char pwdBuffer[ 4096 ];
uid_t myUid = 501;
if ( getpwuid_r( myUid, &pwd, pwdBuffer, 4096, &pwdResult ) == 0 && pwdResult != NULL )
{
lastUid = pwdResult->pw_uid; // Is this safe? YES this is safe, pw_uid has type short and can just be copied
lastUsername = pwdResult->pw_name;
}
}

void linuxTest()
{
uid_t lastUid = 0;
std::string lastUsername("root");
linuxTest(lastUid, lastUsername);
std::cout << "Results for linuxTest - lastUsername: " << lastUsername << std::endl;
}