2016-05-12 5 views
1

Я пишу программу на C++, которая имеет две функции, которые могут работать полностью независимо друг от друга. Поэтому я хочу разбить их на две программы.Как разбить программу на C++ на модули

Проблема в том, что оба они полагаются на один и тот же набор функций (которые могут расти или меняться). То, что я хочу сделать, это включить этот набор функций в качестве заголовка в обе программы, поэтому мне не нужно копировать/вставлять каждый раз, когда я что-то меняю (я также ненавижу дублирующие функции или что-то еще).

Я знаю, это глупый вопрос, но я не мог найти документацию по этому вопросу.

Если код необходим, попросите его, и я отправлю его.

Спасибо!

EDIT:

Добавление кода:

Функции, которые могут работать независимы make_video() и delete_video() Пожалуйста, иметь в виду, что это далеко не сделано.

#include <sys/types.h> 
#include <sys/stat.h> 
#include <iostream> 
#include <fstream> 
#include <sstream> 
#include <cstdlib> 
#include <fcntl.h> 
#include <errno.h> 
#include <unistd.h> 
#include <syslog.h> 
#include <string> 
#include <cstring> 
#include <dirent.h> 
#include <vector> 
#include <time.h> 
#include <csignal> 
#include <algorithm> 
#include <functional> 
#include <cctype> 
#include <locale> 

#include <mysql/mysql.h> 
#include <mysql_connection.h> 
#include <mysql_driver.h> 
#include <cppconn/driver.h> 
#include <cppconn/exception.h> 
#include <cppconn/resultset.h> 
#include <cppconn/statement.h> 

struct config_t{ 
    std::string sql_host; //mysql host 
    std::string sql_user; //mysql user 
    std::string sql_pass; //mysql password 
    std::string sql_db; //zoneminder database name 
    std::string sql_ev_zm; //zoneminder events table 
    std::string sql_ev_vid; //video events table 
    std::string sql_ev_videxp; //video events expiration table 
    std::string sql_mon; //zm monitors table 

    std::string dir_ev; //Zoneminder events directory 
    std::string dir_vid; //manager videos directory 
    std::string dir_ram; //Ramfs mount directory 
    std::string ram_size; //ramfs size 
}; 

int is_dir(const char *pathname){ 
    struct stat info; 

    if(stat(pathname, &info) != 0) 
     return -1; 
    else if(info.st_mode & S_IFDIR) // S_ISDIR() doesn't exist on my windows 
     return 1; 
    else 
     return 0; 


} 

// trim from start (in place) 
static inline void ltrim(std::string &s) { 
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace)))); 
} 

// trim from end (in place) 
static inline void rtrim(std::string &s) { 
    s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end()); 
} 

// trim from both ends (in place) 
static inline void trim(std::string &s) { 
    ltrim(s); 
    rtrim(s); 
} 

// trim from start (copying) 
static inline std::string ltrimmed(std::string s) { 
    ltrim(s); 
    return s; 
} 

// trim from end (copying) 
static inline std::string rtrimmed(std::string s) { 
    rtrim(s); 
    return s; 
} 

// trim from both ends (copying) 
static inline std::string trimmed(std::string s) { 
    trim(s); 
    return s; 
} 

bool DirectoryExists (const char* path){ 

    if(path == NULL)return false; 
    DIR *d; 
    d = opendir(path); 
    if (d){ 
     closedir(d); 
     return true; 
    } 
    return false; 
} 

std::ifstream::pos_type filesize(const char* filename){ 
    std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary); 
    return in.tellg(); 
} 

bool mount_ramfs(config_t *conf){ 
    return false; 
} 


bool make_video(config_t *conf, std::string path = "", int depth = 0){ 
    try{ 
     sql::mysql::MySQL_Driver *driver; 
     sql::Connection *con; 
     sql::Statement *stmt; 
     sql::ResultSet *res; 

     driver = sql::mysql::get_mysql_driver_instance(); 
     con = driver->connect(conf->sql_host, conf->sql_user, conf->sql_pass); 
     stmt = con->createStatement(); 
     stmt->execute("USE " + conf->sql_db); 

     std::string query = "SELECT Id, MonitorId, StartTime, EndTime, Length, Frames FROM " + conf->sql_ev_zm + " WHERE EndTime IS NOT NULL LIMIT 10"; //select a bunch of events for processing, EndTime NOT NULL means that the event is complete and not corrupted 

     //syslog (LOG_DEBUG, "Mysql Query: %s", query.c_str()); 

     res = stmt->executeQuery(query); 


     // SELECT Id, MonitorId, StartTime FROM Events WHERE StartTime < NOW() - INTERVAL 1 DAY ORDER BY Id ASC LIMIT 1 
     // SELECT Id, MonitorId, StartTime FROM Events WHERE StartTime LIKE "%2015-11-18%" and MonitorId = 56 
     // conf.dir_ev + "/" + MonitorId + "/" + todir(StartTime) 

     while (res->next()) { 
      int id = res->getInt("Id"); 
      int monitor = res->getInt("MonitorId"); 
      std::string start_time = res->getString("StartTime"); 
      std::string end_time = res->getString("EndTime"); 
      int lenght = res->getInt("Length"); 
      int frames = res->getInt("Frames"); 

      //Get event directory form table data; the dir structure is id/YY/MM/DD/HH/mm/ss/ 

      char sttm[60] = {}; 
      std::strcpy(sttm, start_time.c_str()); 
      char * tkn = (char*)malloc(60); 
      std::stringstream pathSS; 

      pathSS << conf->dir_ev << '/' << id << '/'; //prepare the path 

      tkn = std::strtok (sttm," -:"); //here we tokenize the StartTime field to match the source directory structure 
      if (tkn != NULL) { 
       pathSS << tkn[2] << tkn[3] << '/'; 
       tkn = std::strtok (NULL, " -:"); 
      } 
      while (tkn != NULL) { 
       pathSS << tkn << '/'; 
       tkn = std::strtok (NULL, " -:"); 
      } 
      std::string src = pathSS.str(); 
      pathSS.clear(); 


      pathSS << conf->dir_vid << '/' << id << '/'; //prepare the path 
      std::string dest = pathSS.str(); 
      pathSS.clear(); 

      tkn = std::strtok (sttm," -:"); //here we tokenize the StartTime field to make the destinantion and filename 
      while (tkn != NULL) { 
       pathSS << tkn << '_'; //sadly this will always lead to "_.mp4" but its not that bad or important 
       tkn = std::strtok (NULL, " -:"); 
      } 
      pathSS << ".mp4"; 
      std::string fname = pathSS.str(); 
      pathSS.clear(); 

      //do event video and 
      /*std::string cmd = "mkdir -p " + conf->dir_vid + path; 
      syslog (LOG_DEBUG, "%s", cmd.c_str()); 
      std::system(cmd.c_str()); 
      cmd = "tar -zcf " + conf->dir_vid + path + "/" + dirlist[i] +".tar.gz " + conf->dir_ev + path + "/" + dirlist[i]; 
      syslog (LOG_DEBUG, "%s", cmd.c_str()); 
      std::system(cmd.c_str()); 
      cmd = "rm -rf " + conf->dir_ev + src + "*"; 
      syslog (LOG_DEBUG, "%s", cmd.c_str()); 
      std::system(cmd.c_str());*/ 
      try{ 
       //insert new row in videos table 
       pathSS << "INSERT INTO " << conf->sql_ev_vid << " (startTime, endTime, monitor, filename) VALUES (\'" << start_time << "\', \'" << end_time << "\', " << monitor << ",\'" << dest << fname << "\')"; 
       stmt->execute(pathSS.str()); 
       pathSS.clear(); 
       //delete non existing event 
       pathSS << "DELETE FROM " << conf->sql_ev_zm << " WHERE Id = " << id; 
       stmt->execute(pathSS.str()); 
       pathSS.clear(); 
      } 
      catch(sql::SQLException &e){ 
       syslog (LOG_ERR, "Mysql Exception: %s, ERRNO %i, MySQL State: %s", e.what(), e.getErrorCode(), std::string(e.getSQLState()).c_str()); 
       exit(EXIT_FAILURE); 
      } 

     } 
     delete res; 
     delete stmt; 
     delete con; 
    } 
    catch(sql::SQLException &e){ 
     syslog (LOG_ERR, "Mysql Exception: %s, ERRNO %i, MySQL State: %s", e.what(), e.getErrorCode(), std::string(e.getSQLState()).c_str()); 
     exit(EXIT_FAILURE); 
    } 
    return true; 
} 

bool delete_video(config_t *conf){ 
    try{ 
     sql::mysql::MySQL_Driver *driver; 
     sql::Connection *con; 
     sql::Statement *stmt; 
     sql::ResultSet *res; 
     sql::ResultSet *subres; 

     driver = sql::mysql::get_mysql_driver_instance(); 
     con = driver->connect(conf->sql_host, conf->sql_user, conf->sql_pass); 
     stmt = con->createStatement(); 
     stmt->execute("USE " + conf->sql_db); 

     std::string query = "SELECT monitor, recording_days FROM " + conf->sql_ev_videxp + " WHERE 1"; 

     //syslog (LOG_DEBUG, "Mysql Query: %s", query.c_str()); 

     res = stmt->executeQuery(query); 


     // SELECT Id, MonitorId, StartTime FROM Events WHERE StartTime < NOW() - INTERVAL 1 DAY ORDER BY Id ASC LIMIT 1 
     // SELECT Id, MonitorId, StartTime FROM Events WHERE StartTime LIKE "%2015-11-18%" and MonitorId = 56 
     // conf.dir_ev + "/" + MonitorId + "/" + todir(StartTime) 

     while (res->next()) { 
      int id = res->getInt("Id"); 
      int r_days = res->getInt("recording_days"); 

      //syslog (LOG_DEBUG, "Id: %i, Recording Days: %i", id, r_days); 

      std::stringstream subQuerySS; 
      subQuerySS << "SELECT id, file FROM " << conf->sql_ev_vid << " WHERE date < NOW() - INTERVAL " << r_days << " DAY AND monitor = " << id; 
      std::string subQuery = subQuerySS.str(); 
      subQuerySS.clear(); 

      //syslog (LOG_DEBUG, "Mysql Query: %s", subQuery.c_str()); 

      subres = stmt->executeQuery(subQuery); 

      while (subres->next()) { 
       int subid = subres->getInt("id"); 
       std::string file = subres->getString("file"); 

       std::string cmd = "rm -f " + file; 
       syslog (LOG_DEBUG, "%s", cmd.c_str()); 
       std::system(cmd.c_str()); 

       std::stringstream delQuerySS; 
       delQuerySS << "DELETE FROM " << conf->sql_ev_vid << " WHERE id = " << subid; 
       std::string delQuery = delQuerySS.str(); 
       delQuerySS.clear(); 

       syslog (LOG_DEBUG, "Mysql Query: %s", delQuery.c_str()); 

       stmt->execute(delQuery); 
      } 

     } 
     delete res; 
     delete subres; 
     delete stmt; 
     delete con; 
    } 
    catch(sql::SQLException &e){ 
     syslog (LOG_ERR, "Mysql Exception: %s, ERRNO %i, MySQL State: %s", e.what(), e.getErrorCode(), std::string(e.getSQLState()).c_str()); 
     exit(EXIT_FAILURE); 
    } 
    return true; 
} 

void signalHandler(int signum){ 
    syslog (LOG_NOTICE, "signal received (%i)", signum); 

    closelog(); 

    exit(signum); 
} 

int main(void) { 

     /* Our process ID and Session ID */ 
     pid_t pid, sid; 


     /* Fork off the parent process */ 
     pid = fork(); 
     if (pid < 0) { 
       exit(EXIT_FAILURE); 
     } 
     /* If we got a good PID, then 
      we can exit the parent process. */ 
     if (pid > 0) { 
       std::ofstream pid_file; 
       pid_file.open("evmanager.pid", std::ofstream::trunc); 
        if(pid_file.is_open()){ 
         pid_file << pid; 
         pid_file.close(); 
        } 

       exit(EXIT_SUCCESS); 
     } 

     /* Change the file mode mask */ 
     umask(0); 

     setlogmask (LOG_UPTO (LOG_DEBUG)); 

     openlog ("dt_event_manager", LOG_PID, LOG_DAEMON); 

     syslog (LOG_NOTICE, "Program started by User %d", getuid()); 

     /* Create a new SID for the child process */ 
     sid = setsid(); 
     if (sid < 0) { 
       syslog (LOG_ERR, "SID Creation Failed"); 
       exit(EXIT_FAILURE); 
     } 



     /* Change the current working directory */ 
     if ((chdir("/")) < 0) { 
       syslog (LOG_ERR, "Failed while Changing directory to /"); 
       exit(EXIT_FAILURE); 
     } 

     /* Read Initial Configuration */ 
     std::ifstream conf_file; 
     std::string line; 
     config_t conf; 

     conf_file.open("/etc/zm/evmanager.conf"); 
     if(conf_file.is_open()){ 
      while(std::getline(conf_file, line)){ 
       if(line[0] == '#')continue; 
       std::istringstream is_line(line); 
       std::string key; 
       if(std::getline(is_line, key, '=')){ 
        std::string value; 
        if(std::getline(is_line, value)){ 
         trim(key); 
         trim(value); 
         if(key == "sql_host")conf.sql_host = value; //mysql host 
         else if(key == "sql_user")conf.sql_user = value; //mysql user 
         else if(key == "sql_pass")conf.sql_pass = value; //mysql password 
         else if(key == "sql_db")conf.sql_db = value; //zoneminder database name 
         else if(key == "sql_ev_zm")conf.sql_ev_zm = value; //zoneminder events table 
         else if(key == "sql_ev_vid")conf.sql_ev_vid = value; //video events table 
         else if(key == "sql_ev_videxp")conf.sql_ev_videxp = value; //Zoneminder videos expiration directory 
         else if(key == "sql_mon")conf.sql_mon = value; //Zoneminder Monitors directory 
         else if(key == "dir_ev")conf.dir_ev = value; //Zoneminder events directory 
         else if(key == "dir_vid")conf.dir_vid = value; //Manager Videos directory 
         else if(key == "dir_ram")conf.dir_ram = value; //Ramfs mount dir 
         else if(key == "ram_size")conf.ram_size = value; //Ramfs size 
         else{ 
          syslog (LOG_ERR, "Bad config readout"); 
          exit(EXIT_FAILURE); 
         } 
        } 
       } 
      } 
     } 
     else{ 
      syslog (LOG_ERR, "Failed to open configuration file"); 
      exit(EXIT_FAILURE); 
     } 


     /* Close out the standard file descriptors */ 
     close(STDIN_FILENO); 
     close(STDOUT_FILENO); 
     close(STDERR_FILENO); 

     /* Daemon-specific initialization goes here */ 

     /* The Big Loop */ 

     signal(SIGINT, signalHandler); 

     syslog (LOG_INFO, "Daemon Started"); 

     while (1) { 
      make_video(); 
      delete_video(); 

      sleep(10); /* wait 10 seconds */ 
     } 
    exit(EXIT_SUCCESS); 
} 
+2

Невозможно создать 1 программу с параметрами запуска и запустить ее дважды? – guiguiblitz

+0

никогда не думал об этом, блестящий! Но это служба linux, поэтому я не знаю, как выйти из родительского процесса и запустить ребенка с теми же параметрами, больным придется провести некоторое тестирование. – Arheisel

+1

, пожалуйста, пришлите код, теперь неясно. –

ответ

2

Это отличный вопрос. Я действительно сильно решу эту проблему со студентами, поскольку я был репетитором на C++.

В качестве примера, скажем, если это был ваш главный:

//main.cpp: 
#include <iostream> 
#include "add.h" /// This comes in later :) 

using namespace std; 
int main() 
{ 
    cout << "2+3=" << add(2,3) << endl; 
    return 0; 
} 

Вам нужно создать файл заголовка для функции добавления, например:

//add.h 
#ifndef ADD_H_INCLUDED 
#define ADD_H_INCLUDED 
int add(int a, int b); 
#endif // ADD_H_INCLUDED 

Но тогда вам нужно на самом деле определите, что делает функция добавления:

//add.cpp 
#include "add.h" 
int add(int a, int b) { 
    return a + b; 
} 

Теперь вы можете выбрать несколько функций i n один файл заголовка, или каждая функция может иметь свой собственный файл заголовка, но это ваш выбор :) Надеюсь, это поможет!

+0

Спасибо! Нужно ли иметь какое-то отношение к команде g ++? – Arheisel

+0

Чтобы скомпилировать его, используйте: «g ++ main.cpp add.cpp», и он должен работать. Не компилируйте файл заголовка. –

+0

Мы никогда не компилируем файлы .h. Если вы это сделаете, оно может скомпилироваться, но оно дает вам нежелательный файл, который вы не можете использовать. С g ++ просто скомпилируйте файлы .cpp. –

2

У вас есть несколько вариантов. Проще всего создать несколько файлов, возможно, как это:

shared_header.hpp 
shared_functionality.cpp 
prog_a.cpp 
prog_b.cpp 

Затем программа А скомпилирован с источниками prog_a.cpp и shared_functionality.cpp, и программа B скомпилирован с источниками prog_b.cpp и shared_functionality.cpp. Все исходные файлы включают заголовок, а prog_a.cpp и prog_b.cpp определяют свою собственную функцию main.

Смежные вопросы