0

Okay, maybe I'm doing something fatally stupid, but I'm mad. All day I've been dealing with vectors storing in them pointers to my own class, but they mess up (much of the time). Sometimes when I traverse through them, I end up getting the other vector's variable, other times I get some complete nonsense from memory.

Here's some of the code:

vector<TCPClientProtocol*> clients;
vector<TCPClientProtocol*> robots;

//this function gets names from "robots" and sends them to all the "clients"
void sendRobotListToClients(){
    //collect the list:
    int numRobots = robots.size();
    char *list = (char*)malloc(numRobots * USERNAME_SIZE);
    for(int i=0; i<numRobots; i++){
        int namelen = strlen(robots[i]->name);
        memcpy(&list[i*USERNAME_SIZE], robots[i]->name,
            namelen);
        if(namelen < USERNAME_SIZE)
            list[i*USERNAME_SIZE + namelen] = (char)0;
    }

    //send it to all clients:
    int numClients = clients.size();
    for(int i=0; i<numClients; i++){
        int result = clients[i]->sendRobotList(list, numRobots);
        if(result < 0){
            cout<<"Failed sending refreshed list to "
                <<clients[i]->name<<"."<<endl;
        }
    }

    delete list; //forgot to add this before
}

//How I created vectors:
vector<TCPClientProtocol*> clients;
vector<TCPClientProtocol*> robots;

//and this is how I add to them:
robots.push_back(robot);

Basically, I'm not getting the memory I want. I'm considering going to arrays, or making my own class, but I wanted dynamic storage. This is silly, though...

robots.push_back(robot1);
clients.push_back(client1);

As an example:

TCPClientProtocol *robot = new TCPClientProtocol(mySocket); //create with existing socket
robot->name = "robot1";
cout<<robot->name<<endl; //prints correctly
robots.push_back(robot);
... //do some other stuff (this IS multithreaded, mind you)
cout<<robots[0]->name<<endl; //prints something strange

The TCPClientProtocols are derived from a listening server socket that returns sockets and puts them into the class. While the pointers sit inside the vectors, I use socket functions in the class, i.e

robot->sendData(buffer, lenght);
robot->receiveData(buffer, length);

etc. Afterwards, I go try to reference them again. I can't put all the code here... it's over 500 lines long.

Then I collect the robot names, and I either get gibbrish, or the client's name. Anyway, thanks for your help.

EDIT: I deliberately tested it to see exactly what it was doing at every step. It printed out the exact name/string (robot->name) that I wanted. After it was pushed into the vector, however, I took that exact same pointer from within the vector, it no longer pointed to the right name, instead gave me something else entirely. That's why I'm confused. My apparently bad memory manipulation works well enough when vectors aren't involved.

The function that adds directly to the vector:

void addRobotToList(TCPClientProtocol *robot){
    //add robot to list
    robots.push_back(robot);
    cout<<"Added "<<robot->name<<endl;
}

The function that calls this function (warning: long!) - and yes, I mean to divide it up but this is sort of a draft:

DWORD WINAPI AcceptThread(void* parameter){
TCPClientProtocol* cl = (TCPClientProtocol*)parameter;

TCPHeader *head = new TCPHeader;
loginInfo *logInfo = new loginInfo;

//Read header.
int result = cl->receiveHeader(head);
if(result < 0)
    return -1;
//Check data. Expected: DATATYPE_CONNETION_REQUEST
//  and check protocol version.
if( head->version != (char)PROTOCOL_VERSION ||
    head->type != (char)DATATYPE_CONNECTION_REQUEST ||
    head->size != (int)CONNECTION_REQUEST_LENGTH){
        goto REJECT;
}

cout<<"Accepted connection."<<endl;

result = cl->requestLoginInfo();
if(result < 0)
    goto CONNECTIONLOST;

//Read header.
result = cl->receiveHeader(head);
if(result < 0)
    goto CONNECTIONLOST;
if(head->type != DATATYPE_LOGIN_INFO){
    goto REJECT;
}

//read login information
result = cl->receiveLoginInfo(logInfo);
if(result < 0)
    goto CONNECTIONLOST;

//check for authentication of connector. If failed, return.
if(!authenticate(logInfo)){
    goto REJECT;
}

cout<<"Authenticated."<<endl;

//add name to robot/client
cl->name = logInfo->username;

//Check for appropriate userType and add it as a variable:
switch(logInfo->userType){
case USERTYPE_ROBOT:
    cl->userType = USERTYPE_ROBOT;
    cl->isClient = false;
    cout<<"Robot connected: "<<cl->name<<endl;
    break;
case USERTYPE_CLIENT:
    cl->userType = USERTYPE_CLIENT;
    cl->isClient = true;
    cout<<"Client connected: "<<cl->name<<endl;
    break;
default:
    goto REJECT;
    break;
}

//Send a phase change to PHASE 2:
result = cl->notifyPhaseChange(2);
if(result < 0)
    goto CONNECTIONLOST;

//if client, send robot availability list and listen for errors
//  and disconnects while updating client with refreshed lists.
if(cl->isClient){
    //add client to clients list:
    clients.push_back(cl);

    //send initial list:
    int numRobots = robots.size();
    char *list = (char*)malloc(numRobots * USERNAME_SIZE);
    for(int i=0; i<numRobots; i++){
        cout<<(i+1)<<" of "<<numRobots<<": "<<robots[i]->name<<endl;
        int namelen = strlen(robots[i]->name);
        memcpy(&list[i*USERNAME_SIZE], robots[i]->name,
            namelen);
        if(namelen < USERNAME_SIZE)
            list[i*USERNAME_SIZE + namelen] = (char)0;
    }
    result = cl->sendRobotList(list, numRobots);
    if(result < 0){
        removeClientFromList(cl->name);
        goto CONNECTIONLOST;
    }

    cout<<"Sent first robot list."<<endl;

    //wait to receive a ROBOT_SELECTION, or error or disconnect:
    result = cl->receiveHeader(head);
    if(result < 0){
        removeClientFromList(cl->name);
        goto CONNECTIONLOST;
    }
    if(head->type != DATATYPE_ROBOT_SELECTION){
        removeClientFromList(cl->name);
        goto REJECT;
    }

    //receive and process robot selection
    char *robotID = (char*)malloc(ROBOT_SELECTION_LENGTH+1);
    result = cl->receiveRobotSelection(robotID);
    robotID[USERNAME_SIZE] = (char)0;
    robotID = formatUsername(robotID);
    if(result < 0){
        removeClientFromList(cl->name);
        goto CONNECTIONLOST;
    }

    cout<<"Got a selection.."<<endl;

    //get the robot and remove it from list
    TCPClientProtocol *robot = removeRobotFromList(formatUsername(robotID));
    cout<<"Removal win."<<endl;
    //check robot status:
    if(robot == NULL){
        //TRY AGAIN
        cout<<"Oh mai gawsh, robot is NULL!"<<endl;
        getch();
    }
    else if(!robot->tcpConnected()){
        //TRY AGAIN
        cout<<"Oh mai gawsh, robot DISCONNECTED!"<<endl;
        getch();
    }else{
        cout<<"Collected chosen robot: "<<robot->name<<endl;
    }

    //request stream socket information from client
    result = cl->requestStreamSocketInfo();
    if(result < 0){
        removeClientFromList(cl->name);
        addRobotToList(robot); //re-add the robot to availability
        goto CONNECTIONLOST;
    }

    result = cl->receiveHeader(head);
    if(result < 0){
        removeClientFromList(cl->name);
        addRobotToList(robot); //re-add the robot to availability
        goto CONNECTIONLOST;
    }

    //check for datatype
    if(head->type != DATATYPE_STREAM_SOCKET_INFO){
        removeClientFromList(cl->name);
        addRobotToList(robot); //re-add the robot to availability
        goto REJECT;
    }
    //receive stream socket info:
    char *ip = (char*)malloc(20);
    int port;
    result = cl->receiveStreamSocketInfo(ip, &port);
    if(result < 0){
        removeClientFromList(cl->name);
        addRobotToList(robot); //re-add the robot to availability
        goto CONNECTIONLOST;
    }

    cout<<"Got ip: "<<ip<<" port: "<<port<<endl;

    //send stream socket information to robot
    result = robot->sendStreamSocketInfo(ip, port);
    if(result < 0){
        //RETURN CLIENT TO 'step 5'
        removeClientFromList(cl->name);
        delete robot;
        goto CONNECTIONLOST;
    }

    //send phase changes to both, and use this thread
    //  to monitor signals from client to robot.
    result = cl->notifyPhaseChange(3);
    if(result < 0){
        addRobotToList(robot); //re-add the robot to availability
        removeClientFromList(cl->name);
        goto CONNECTIONLOST;
    }
    result = robot->notifyPhaseChange(3);
    if(result < 0){
        //RETURN CLIENT TO 'step 5'
        removeClientFromList(cl->name);
        delete robot;
        goto CONNECTIONLOST;
    }

    cout<<"PHASE 3 INITIATED"<<endl;
    removeClientFromList(cl->name);

    //run a thread sending connections from CLIENT to ROBOT.
    while(true){
        cout<<"Listening for header..."<<endl;
        //read next header from client
        result = cl->receiveHeader(head);
        cout<<"Got something"<<endl;
        if(result < 0){
            cout<<"Failed read."<<endl;
            delete robot;
            goto CONNECTIONLOST;
        }
        if(head->type != DATATYPE_COMMAND){
            cout<<"Not a command. Protocol mismatch"<<endl;
            continue;
        }

        cout<<"Gots header"<<endl;

        //read command
        result = cl->receiveCommand();
        if(result < 0){
            //RESET ROBOT!
            delete robot;
            goto CONNECTIONLOST;
        }

        cout<<"Got data."<<endl;

        result = robot->sendCommand((char)result);
        if(result < 0){
            //RESET CLIENT!
            delete robot;
            goto CONNECTIONLOST;
        }
    }

    //spawn a thread for robot-to-client and client-to-robot comm,
    //  possibly just client-to-robot.
    //send a phase change (to phase 3) - in thread!
}

//if robot, add to robot list and wait.
else{
    //add robot to robots list:
    addRobotToList(cl);
}

delete head;
delete logInfo;
return 0;

//Clean up variables and send reject message
REJECT:
cout<<"Connection rejected."<<endl;
cl->sendRejection();
delete cl;
delete head;
delete logInfo;
return -1;

CONNECTIONLOST:
cout<<"Connection lost."<<endl;
delete cl;
delete head;
delete logInfo;
return -1;
}
Sefu
  • 2,404
  • 8
  • 42
  • 59
  • 1
    how do you store pointers in vector - sample of the code pleas? – matekm Aug 09 '11 at 07:22
  • 2
    could you also clarify where you get the data messed up? is it in `sendRobotList`? – sergio Aug 09 '11 at 07:26
  • 3
    It is extremely unlikely that the vectors are misbehaving. It is more likely that you misunderstand how they are supposed to behave, and/or that you made a mistake in your code. In this case, the mistake is likely in the way you fill the vectors. – Sander De Dycker Aug 09 '11 at 07:28
  • I merely did a "push_back(item)" on them. From the documentation, that's what I understood you're supposed to do, but of course, this is the first time I've worked with vectors, so I don't know. – Sefu Aug 09 '11 at 07:34
  • Did you `push_back(item)` or `push_back(&item)`. You have declared the vector as `vector` so it should be the latter. – DanS Aug 09 '11 at 07:44
  • I just did `push_back(item)` because item is already a pointer. For example: `item *i = ...; push_back(item);` With the & it shows an error. – Sefu Aug 09 '11 at 07:53
  • 1
    Show the full function where the pointers are added to the vector. – Benjamin Lindley Aug 09 '11 at 07:54
  • How is `TCPClientProtocol::name` declared? – Ajay Aug 09 '11 at 07:55
  • You are leaking memory out of the function... you need to `free` the `list` pointer. As of the actual error, it is probably somewhere else in the code. You should check/show the code that creates the objects and inserts them into the vector, where you got the pointers from (I hope not from the stack) and what you do with the pointers after insertion into the vector. The issue is not with the vector but with code that you are not showing here. – David Rodríguez - dribeas Aug 09 '11 at 07:58
  • `TCPClientProtocol::name` is a public pointer inside the class. I assigned it a name by setting it to a string in another functions (= operator). Can this be the fault? However, as I said, it works fine when using the pointer before placing it into the vector, which is why this confuses me. It only fails when the vectors are involved. – Sefu Aug 09 '11 at 07:59
  • Again I say, show the full function where the pointers are added to the vector, not a small "example" which we have no way of knowing if it accurately reflects the relevant parts of the code. – Benjamin Lindley Aug 09 '11 at 08:18
  • @BenjaminLindley Okay done. It's very long... and I'm sure it's riddled with silly errors, but it works for now (for everything else, anyway). If you haven't guessed already, I'm quite new to C++ and this whole memory-oriented programming. – Sefu Aug 09 '11 at 08:30
  • I don't see in that function where you used cout to check the string before and after you added it to the vector. On a side note, if you're new to C++, where are you learning from, and why are you trying such advanced subjects like threading when you don't have the basics down? What is memory-oriented programming? And why do you use so much dynamic memory allocation? – Benjamin Lindley Aug 09 '11 at 08:54
  • Sorry, it did print it right after the push - I swear - I accidentally cropped in while posting it here. Fixed. I started with Java and a little bit of basic Python. I've done all this in Java before, it's very easy with ArrayLists. Multithreading too. I'm doing this because of a summer project, and I'm doing what needs to be done for it, learning in the process. "Memory-oriented" as in everything in C++ seems to be memory, unlike Java where it's all objects. I do all that memory allocation because because that's how I learned it I guess. It feels more natural to me... – Sefu Aug 09 '11 at 09:00
  • @Richard: C++ can be just as object oriented as Java. There are very few reasons to ever use the keyword `new`, and for most people, no reason to *ever* use the keyword `delete`. If that's how you are learning C++, then you are learning it wrong. [Get one of these books](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) and learn the language properly. – Benjamin Lindley Aug 09 '11 at 09:08
  • I checked out those books. Thanks, they are really good! I think they will definitely help. Though I still can't pinpoint exactly where my problem here is with the vectors, I will try to read and learn the right format and take into account all the suggestions here, and re-write a lot of my existing code. Hopefully the problem(s) will vanish. – Sefu Aug 09 '11 at 14:19

3 Answers3

5

You code is a horrible mix of C and C++ and of course all the errors are in the C parts :). So don't blame vectors but instead look at all that horrible low level memory manipulation.

Looks to me that

  • There's no guarantee that you won't overflow the bounds of list
  • There's no guarantee that list will be null terminated
  • The list memory leaks

Start using std::string would seem to be the best advice.

john
  • 85,011
  • 4
  • 57
  • 81
  • ..or `ostringstream` to generate the "list" – Nim Aug 09 '11 at 07:28
  • The strings are all null-terminated. I wrote my own code. The reason I'm using char* instead of strings is because I send all this stuff through sockets using a protocol class that deals with bytes/chars to send everything. – Sefu Aug 09 '11 at 07:32
  • @Richard: Just to send them out as `char`, You do need need to store them as `char`. – Alok Save Aug 09 '11 at 07:33
  • @Richard: then you convert it to a byte array before sending. With the same argument you could use assembler, since it ends up in assembler anyway after compiling :) – duedl0r Aug 09 '11 at 07:34
  • 3
    @Richard, that's no reason not to use string. Just convert to and from char arrays (or vectors of char) at the point where you send/recieve the strings. Direct memory manipulation is hard, you're not the first programmer to get it wrong, that's **why** we have vector, string etc. – john Aug 09 '11 at 07:37
3

Some of the things to point out with one look at the code here will be:

  • You should be using new instead of malloc.
  • You should be using smart pointers not storing raw pointers in your vector.
  • Use iterators for iterating over the vector contents.
  • Use std::string and not char*
Alok Save
  • 202,538
  • 53
  • 430
  • 533
1

SOLVED: The error was deleting the loginInfo struct lead to erasing the name object inside of it, thus invalidating the pointers in the name variable. Thanks to all who recommended using strings, they definitely solved the problem and they're not as risky to use.

Sefu
  • 2,404
  • 8
  • 42
  • 59