c++ - 如何将 C++ 程序分解为模块

标签 c++ module header-files

我正在编写一个 C++ 程序,该程序具有两个可以完全独立运行的函数。所以我想把它们分成两个程序。

问题在于它们都依赖于同一组功能(可以增长或改变)。我想要做的是将这组函数作为标题包含在两个程序中,这样我就不必在每次更改某些内容时都进行复制/粘贴(我也讨厌复制函数或任何东西)。

我知道这是一个愚蠢的问题,但我找不到关于此事的任何文档。

如果需要代码,请索取,我会发布。

谢谢!

编辑:

添加代码:

可以独立运行的函数有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);
}

最佳答案

这是一个很好的问题。事实上,我经常和学生一起解决这个问题,因为我是一名 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函数创建一个头文件,像这样:

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

但是您需要实际定义 add 函数的实际作用:

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

现在您可以选择将多个函数放在一个头文件中,或者每个函数可以有自己的头文件,但这是您的选择:)希望这对您有所帮助!

关于c++ - 如何将 C++ 程序分解为模块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37189303/

相关文章:

c++ - 如何迭代 long long 的二进制掩码?

android - Android 两个模块之间的通信

qt - 将外部 QML 模块路径添加到 QQmlApplicationEngine

android - 在所有模块上运行 android checkstyle

c - 在 MSVS 2012 Express 中构建 OpenCl 示例时出现问题。没有找到头文件,但找到路径等...看起来不错

c++ - 在 .cpp 文件中包含头文件

opengl - VS2012C++ : Hundreds of errors when compiling glee.h file

C++ 右值引用转发性能

c++ - 为什么使用集合而不是 map ? C++

c++ - 将迭代器设置为具有最高键的值