2017-11-02 5 views
0

를 반환 : 클래스 외부 (속성에 의해 객체의 벡터를 검색하고 내가 벡터, 사용자 정의 클래스 형의 회원이 UserAccounts, 사용자가 반복자

class User{ 

public: 

//CONSTRUCTORS 

User(){} 

User(std::string username, std::string password)  
    //more efficient than using assignment operators 
    : username(username), password(password), GBPbal(0), sharesbal(0){} 

//DESTRUCTOR  
~User(){} 

//SETTERS 
void changeusername(std::string newuser){username = newuser;} 
void changepassword(std::string newpassword){password = newpassword;} 
void setGBP(float GBP){GBPbal = GBP;} 
void setshares(int shares){sharesbal = shares;} 

//GETTERS (HOLDINGS) 
float getGBPbal(){return GBPbal;} 
int getsharesbal(){return sharesbal;} 
std::string getusername(){return username;} 

//UPDATE HOLDINGS 
float GBPUpdate(float adjustment){ 
GBPbal += adjustment; 
return GBPbal; 
} 
int sharesUpdate(float adjustment){ 
sharesbal += adjustment; 
return sharesbal; 
} 


protected: 

private: 



//Credentials 
std::string username; 
std::string password; 

//Balances 
float GBPbal; 
int sharesbal; 

friend class boost::serialization::access; 
template <typename Archive> 
void serialize(Archive &ar, const unsigned int version) 
{ 
ar & username; 
ar & password; 
ar & GBPbal; 
ar & sharesbal; 
} 
}; 

내가 문자열 주에서 선언 '이름'을 가지고을)에 이미 값이 할당되어 있습니다.

나의 목표는 사용자 이름 === 위에서 언급 한 사용자 이름을 가진 특정 사용자 객체를 벡터에서 검색하고 그것에 대한 참조를 반환합니다. 그것의 GBPbal을 인쇄 할 수 있고,이 값을 공유하고 업데이트 할 수 있어야합니다. 내 연구에서 대답은 다음과 같습니다 :

 auto it = std::find_if(UserAccounts.begin(), 
UserAccounts.end(), [&username](User& obj)->bool {return 
obj.getusername() == username;}); 
    //return their user account 
    std::cout << it->getGBPbal(); 

그러나 이것은 런타임시 작동하지 않는 것으로 보입니다. 나는 'obj'부분을 실제로 이해하지 못한다. 그러나 이것은 벡터 내부의 인스턴스/객체가 명시 적으로 명명되지 않았기 때문에 이것이라고 가정한다.

모든 것이 서로 연결되어 있기 때문에 최소한의 예를 제공하는 것은 어렵습니다.

//INTERNET DOMAIN SERVER: TCP. 
//SERVER SIDE 
//argv[1] = Port number for listening. 
//argv[2] = Specify the maximum number of threads manually (optional). 

// C++/STL/UNIX HEADERS 
#include <cstdio> 
#include <cstring> 
#include <unistd.h> 
#include <sys/types.h>    //Datatypes used for sys calls (and used in <sys/socket.h> and <sys/socket.h>). 
#include <sys/socket.h>   //Definitions of structures used for sockets. 
#include <netinet/in.h>   //Contains constants and structures needed for internet domain addresses 
#include <thread> 
#include <vector> 
#include <map> 
#include <string> 
#include <iostream> 
#include <cstdlib> 
#include <mutex> 


//BOOST HEADERS 
#include <boost/archive/text_iarchive.hpp>  //Input archive. 
#include <boost/archive/text_oarchive.hpp>  //Output archive. 
#include <boost/serialization/map.hpp>   //Allows for archiving maps specifically. 
#include <boost/serialization/string.hpp>  //Allows for archiving strings specifically. 
#include <boost/serialization/vector.hpp>  //Allows for archiving vectors specifically. 
#include <boost/filesystem.hpp> 
#include <boost/filesystem/fstream.hpp>   //Filestream for archiving 

//MY HEADERS 
#include "userclass.h" 

//PP DIRECTIVES 
void clientHandler(int sock, std::map<std::string, std::string>& userandPass, std::vector<class User>& UserAccounts); 
void error(const char *msg); 
void userandPassBackup(std::map<std::string, std::string>& userandPass); 
void userandPassLoad(std::map<std::string, std::string>& userandPass); 
template <typename SaveClass> 
void saveData(const std::string filename, const SaveClass &c); 
template <typename LoadClass> 
void loadData(const std::string filename, LoadClass &c);//was having issues with const as second argument 


//THREADING SETTINGS 
unsigned int MAX_THREADS = std::thread::hardware_concurrency(); // MAX THREADS: LET COMPILER DECIDE BASED ON MACHINE RUNNING SERVER 
// const unsigned int MAX_THREADS = atoi(argv[2]);      // OR YOU CHOOSE 
unsigned int CURR_THREADS = 1;   


//LOCKS 
std::mutex UserAccountsM; 
std::mutex userandPassM; 


            // CURRENT NUMBER OF THREADS 

int main(int argc, char *argv[]) 
{  
// USER DATA 
std::map<std::string, std::string> userandPass;          //BANK OF CREDENTIALS 
std::vector<class User> UserAccounts;           //User account details 

//userandPassLoad(std::ref(userandPass));   
loadData< std::map<std::string, std::string> >("userandPassBackup.txt", userandPass); //LOAD IN DATA FROM BACKUP FILE 
loadData<std::vector<User>>("accounts.txt", UserAccounts); 


         //These two variables store the values returned by the socket system call and the accept system call.  
    int sockfd;   // sockfd and newsockfd are file descriptors ... 
    int newsockfd;   // === array subscripts into the file descriptor table (which store pointers to iostreams). 
    unsigned int portno;   // Portno stores the port that the server will allow connections to. 
    socklen_t clilen;  //stores client address (needed for accept system call) 

//  char buffer[256];  // Socket is connected to this buffer. Server reads characters from here. 
    struct sockaddr_in serv_addr, cli_addr; // Structures containing internet addresses (client and server addresses). 
//  int n;     // return value for read/write calls - ie how many characters have been read/written 
    if (argc < 2) { 
     fprintf(stderr,"ERROR, no port provided\n"); 
     exit(1); 
    } 

    // CREATE NEW SOCKET 
    // THREE ARGUMENTS:#1 . ADDRESS DOMAIN OF SOCKET - INTERNET DOMAIN FOR US, AF_UNIX IS AN ALTERNATIVE. 
    //     #2 . TYPE OF SOCKET - EITHER STREAM (CONTINUOUS) OR DATAGRAM (CHUNK BY CHUNK). 
    //     #3 . PROTOCOL: TCP OR UDP. LEAVE 0 AND THE OS WILL DECIDE WHAT IS APPROPRIATE. 
    // SOCKET CALL WILL RETURN AN ENTRY (SMALL INT > 0) TO THE FILE DESCRIPTOR TABLE. 
    // VALUE IS USED BY ALL REFERENCES TO THE SOCKET HEREON. 
    sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sockfd < 0) 
     error("ERROR opening socket"); 

    // SETS ALL BUFFER VALUES TO 0. ARGS ARE POINTER TO BUFFER, BUFFER SIZE. 
    bzero((char *) &serv_addr, sizeof(serv_addr)); 

    // CONVERTS PORT NUMBER DEFINED BY USER FROM A STRING OF NUMBERS TO AN INTEGER 
    portno = atoi(argv[1]); 

    // SERV ADDR IS OF TYPE SOCKADDR_IN WHICH HAS FOUR FIELDS 
    // CODE FOR SOCK FAMILY: ALWAYS SET TO AF_INET 
    serv_addr.sin_family = AF_INET; 
    //SETS IP OF SERVER. INADDR_ANY FETCHES THIS - ITS PRESET. 
    serv_addr.sin_addr.s_addr = INADDR_ANY; 
    // PORT NO: MUST BE CONVERTED FROM HOST BYTE ORDER TO NETWORK BYTE ORDER USING HTONS 
    serv_addr.sin_port = htons(portno); 

    //BINDS SOCKET (OUR PORT) TO ADDRESS - ARGUMENTS ARE SOCKET FILE DESCRIPTOR, POINTER TO STRUCTURE OF TYPE SOCKADDR 
    if (bind(sockfd, (struct sockaddr *) &serv_addr, 
       sizeof(serv_addr)) < 0) 
       error("ERROR on binding"); 

    // LISTEN FOR CONNECTIONS. ARGS: SOCKET FD, SIZE OF BACKLOG QUEUE - IE HOW MANY CONNECTIONS CAN WAIT IN LINE TO BE PROCESSED 
    listen(sockfd,5); 
    clilen = sizeof(cli_addr); 

    //vector of client threads 
    std::vector<std::thread> clientThreads; 

    // MULTITHREADING FOR SIMULTANEOUS CONNECTIONS 
    // extern const int maxthreads = atoi(argv[2]);  //User defined maximum number of threads for server 
    // PUT ACCEPT STATMENT INSIDE INF WHILE LOOP SO SERVER RUNS INDEFINITELY. 
     while (1) { 

      searchAgain: 
    newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); 

      if (newsockfd < 0){ 
       error("ERROR on accept"); 
      } 
//   pid = fork(); 
//   if (pid < 0) 
//    error("ERROR on fork"); 
      if(CURR_THREADS < MAX_THREADS){ 
//    close(sockfd); /* this was big problem before: we kept getting error 'bad file descriptor' on client side. 
      clientThreads.push_back(std::thread(clientHandler, newsockfd, std::ref(userandPass), std::ref(UserAccounts))); 

      goto searchAgain; 
      for(auto &t : clientThreads){ 
      t.join(); 
      } 
      exit(0); 
     } 
     else close(newsockfd); 
    } //end of loop 
    close(sockfd); 
    return 0; 
} 

void clientHandler(int sock,std::map<std::string, std::string>& userandPass, std::vector<class User>& UserAccounts) 
{ 
    int n; 
    char buffer[256]; 

    // Welcome Messages 
    bzero(buffer,256); 
    write(sock, "Welcome to Chris's exchange! Write 'exit' to exit at any time.",255); // NB third argument always this value to prevent overlap. 
    bzero(buffer, 256); 
    loginmenu : 
    write(sock, "Do you wish to login or register a new account?",255); 
    bzero(buffer, 256); 


    while (1) { 
    n = read(sock,buffer,255); // NB blocks until client executes a write. Initially reads login/register request 
// printf("Client message: %s",buffer); //client message 

    if (n < 0) error("ERROR reading from socket"); 
    if(strcmp(buffer, "login\n") == 0){ // \n is extremely important! 
     bzero(buffer, 256); 
      n = write(sock,"Please enter your username.",255); 
      bzero(buffer, 256); 
      // read their username and password 
      n = read(sock,buffer,255); 
      std::string username(buffer); 
      bzero(buffer, 256); 
      n = write(sock,"Please enter your password.",255); 
      bzero(buffer, 256); 
      n = read(sock,buffer,255); 
      std::string password(buffer); 
      bzero(buffer, 256); 
      if(userandPass.find(username)->second == password){ 
      write(sock, "Login successful. Press return to see balances.\n", 255); 
      auto it = std::find_if(UserAccounts.begin(), UserAccounts.end(), [&username](User& obj)->bool {return obj.getusername() == username;}); 
      //return their user account 
      std::cout << it->getGBPbal(); 
      bzero(buffer, 256); 
      read(sock,buffer,255); 
      std::string rtn(buffer); 
      if (rtn == "\n"){ 
      bzero(buffer, 256); 
      goto loginmenu; 
        }   
      } 
      } 
    else if(strcmp(buffer, "register\n") == 0){ 
     bzero(buffer, 256); 
      n = write(sock,"Please enter your desired username",255); 
      // Write code for reading these into strings 
      bzero(buffer, 256); 
      n = read(sock,buffer,255); 
      std::string username(buffer); 
      bzero(buffer, 256); 
      write(sock, "Please enter your desired password", 255); 
      bzero(buffer, 256);   
      n = read(sock,buffer,255); 
      std::string password(buffer); 
      bzero(buffer, 256); 
      //If it exists already try again 
//  std::unique_lock<std::mutex> UPlock(userandPassM); // to be exception safe 
      if (userandPass.find(username) != userandPass.end()){ 
      write(sock, "That username is taken. Please try again.", 255); 
      } 
      // If it doesn't exist, happy days - lets add it to the database. 
      else if (userandPass.find(username) == userandPass.end()){ 
      userandPass.emplace(username, password); 
//  std::unique_lock<std::mutex> UAlock(UserAccountsM); 
      UserAccounts.emplace_back(username, password); 
      //userandPassBackup(userandPass); 
      saveData< std::map<std::string, std::string> >("userandPassBackup.txt", userandPass); // have removed &. why is this needed. i want to pass by reference 
//  UPlock.unlock(); 
      saveData< std::vector<User> >("accounts.txt", UserAccounts); // have removed &. why is this needed. i want to pass by reference 
// UAlock.unlock(); 
      while(1){ 
      write(sock, "Account created successfully. Press return to continue", 255); 
      bzero(buffer, 256); 
      read(sock,buffer,255); 
      std::string rtn(buffer); 
      if (rtn == "\n"){ 
      bzero(buffer, 256); 
      goto loginmenu; 
        } 
        } 
      } 
     } 
    else{ 
    n = write(sock,"Invalid request, please try again. ",255); 
    bzero(buffer, 256); 
    } 

    if (n < 0) error("ERROR writing to socket"); 
    } 
    --CURR_THREADS; 
} 

//ERROR HANDLING 
void error(const char *msg)  // System call error failures: Errors displayed on stderr. 
{ 
    perror(msg); 
    exit(1); 
} 

template <typename SaveClass> 
void saveData(const std::string filename, const SaveClass& c) 
{ 
    // File to be written to 
    boost::filesystem::remove(boost::filesystem::current_path()/filename); 
    boost::filesystem::path myFile = boost::filesystem::current_path()/filename; 
    // Declare an output file stream ofs that writes serialises to our backup file. 
    boost::filesystem::ofstream ofs(myFile); 
    // Declare archive ta that will use our output file stream 
    boost::archive::text_oarchive ta(ofs); 
    // Write to file 
    ta << c; 
    // How many records have been archived? 
    std::cout << c.size() << " records from have been successfully backed up to " << myFile << "\n"; 
} 

template <typename LoadClass> 
void loadData(const std::string filename, LoadClass& c) 
{ 
    boost::filesystem::path myFile = boost::filesystem::current_path()/filename; 
    boost::filesystem::ifstream ifs(myFile); 
    boost::archive::text_iarchive ta(ifs); 
    // Write to file 
    ta >> c; 
    std::cout << c.size() << " records from have been successfully loaded from " << myFile << "\n"; 
} 

NB는 다음 '자동 그것은 라인'후

 std::cout << UserAccounts[1].getusername(); 

과 해당 개체에 대한 올바른 사용자 이름을 반환 내가 좋아하는 전화를 시도했습니다.

+0

이 문제를 디버깅하려면 람다 대신 명명 된 함수를 만듭니다. 그런 다음 함수 포인터를'find_if()'에 전달하십시오. 이제 새로운 명명 된 함수 안에 중단 점을 설정하거나 그 안에'cout' 문을 추가 할 수 있습니다. –

+0

대부분 스레드와 엉망이되었습니다. –

+0

'goto searchAgain' 정말 사용 했나요? –

답변

0

문제가 해결되었습니다. 클래스의 모든 getter 메소드를 상수 메소드로 변경하고 iterator 'it'을 std :: vector :: iterator 유형으로 명시 적으로 선언했습니다. 이제 정보를 검색합니다.

std::vector<User>::iterator it; 
    it = std::find_if(UserAccounts.begin(), UserAccounts.end(),[username](User const& obj)->bool{return obj.getusername() == username;}); 
    if(it != UserAccounts.end()){ 
    std::cout<<"Name = "<<it->getGBPbal() << std::endl; 
    } else{ 
    std::cout<<"Item not Found"<<std::endl; 
    }