IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【在线OJ模拟项目】03——模块代码详解+注释 -> 正文阅读

[C++知识库]【在线OJ模拟项目】03——模块代码详解+注释

图示

1.模块分解

在这里插入图片描述

2.模块调用

在这里插入图片描述

HTTP模块

oj_sever.cpp代码

#include <iostream>
#include <cstdio>
#include <jsoncpp/json/json.h>
#include "httplib.h"
#include "oj_model.hpp"
#include "tools.hpp"
#include "oj_view.hpp"
#include "compile.hpp"
using namespace httplib;
int main()
{  
	OjModel model;//创建一个模板对象
    //1.初始化httplib库的server对象
	Server svr;
	
	//2.提供三个接口,分别处理三个请求
	//2.1获取整个试题列表,get
    //"第一个参数"就是资源路径
	svr.Get("/all_questions",[&model](const Request& rep, Response& resp){
		//2.1.1返回试题列表
        std::vector<Question> questions;
        //输出
        model.GetAllQuestion(&questions);
        //2.1.2填充,提供所有试题
        std::string html;
        OjView::DrawAllQuestions(questions, &html);//当前这个接口返回html
		resp.set_content(html, "text/html");
	});//all_questions是资源路径;lamdar表达式[]是传递的参数,()是参数,{}是lamdar表达式的实现
	//2.2获取单个试题
	//如何标识浏览器想要获取的是哪一个试题
    //\d:表示一个数字[0, 9]
    //\d+:表示多位数字,即可以访问试题11,12,13
	svr.Get(R"(/question/(\d+))", [&model](const Request& req, Response& resp){
        //1.获取url当中关于试题的数字,获取单个试题的信息
        //std::cout << req.matches[0] << " " << req.matches[1] << std::endl;
         std::cout << req.version << " " << req.method << std::endl;
         std::cout << req.path << std::endl;
        Question ques;
        model.GetOneQuestion(req.matches[1].str(), &ques);
        //2.   渲染模板的html文件
        std::string html;
        OjView::DrawOneQuestion(ques, &html);
        resp.set_content(html, "text/html");
	});
	//2.3编译运行
	//也需要知道我是想提交哪一个试题进行编译
    //1.获取试题编号
    //2.通过 试题编号 进而获取 题目内容 填充到ques中
    //3.用户点击submit后从浏览器获取代码,将代码进行decode
    //4.将代码与tail.cpp合并,成src文件
    //5.编译模块进行编译
    //  编译src文件的技术:创建子进程,使用子进程程序替换称为g++来编译源码文件
    //  成功
    //  失败
    //  运行编译后的文件技术:创建子进程,使用子进程程序替换称为编译出来的可执行程序
    //  成功
    //  失败
	svr.Post(R"(/compile/(\d+))", [&model](const Request& req, Response& resp){
	//1.获取试题
        //1.获取试题id,获取当前题目的所有信息
        Question ques;     model.GetOneQuestion(req.matches[1].str(), &ques);
      		  //ques.tail_cpp_----->main函数调用+测试用例
       		 //post方法在提交代码的时候,是经过encode的,要想正常获取浏览器提交的内容,需要进行decode
       		 //decode,使用decode完成的代码和tail.cpp进行合并,产生待编译的源码
       		 //key:value方式,Key是code,value是内容 
      	 	 //vec保存切割的内容
        //2.获取代码的内容
        std::unordered_map<std::string, std::string> body_kv;
        UrlUtil::PraseBody(req.body, &body_kv);
        //3.获取完整的源文件code_tail.cpp
        //std::string src = body_kv["code"] + ques.tail_cpp_;

        //2.构造json对象,交给编译运行模块
        std::string user_code = body_kv["code"];
        Json::Value req_json;
        req_json["code"] = user_code + ques.tail_cpp_;
        req_json["stdin"]="";
        std::cout << req_json["code"].asString() << std::endl; 
        //获取的结果都在resp_json中
        Json::Value resp_json;
        Compiler::CompileAndRun(req_json, &resp_json);
        //获取的返回结果都在resp_json当中
        std::string err_no = resp_json["errorno"].asString();
        std::string case_result = resp_json["stdout"].asString();
        std::string reason = resp_json["reason"].asString();
        //填充,用于返回响应的内容
        std::string html;
        OjView::DrawCaseResult(err_no, case_result, reason, &html);
        resp.set_content(html, "text/html");//设置响应体
	});
    LOG(INFO, "listen_port") << ": 17878" << std::endl;
    svr.set_base_dir("./www");
	//服务端监听起来
	svr.listen("0.0.0.0", 17878);
	return 0;
}

试题模块

oj_model代码

#pragma once

#include <iostream>
#include <string>
#include <fstream>
//#include <boost/algorithm/string.hpp>
#include "tools.hpp"
#include <vector>
#include <unordered_map>//无符号的map来保存多个问题
//描述问题的信息
struct Question{
	std::string id_;//题目ID
	std::string title_;//题目标题
	std::string star_;//题目的难易程度
	std::string path_;//题目路径
    std::string desc_;//题目的描述
    std::string header_cpp_;//题目预定义的头
    std::string tail_cpp_;//题目的尾,包含测试用例以及调用逻辑
};
//从磁盘中加载题目,给用户提供接口查询
class OjModel
{
    public:
    OjModel()
    {
        //拿到配置文件
        Load("./oj_data/oj_config.cfg");
    }
    ~OjModel()
    {

    }
    //1.从文件当中获取题目信息
    bool Load(const std::string& filename)
    {
        //fopen open c++ fstream
        //文件数去流
        std::ifstream file(filename.c_str());
        //如果打开失败了
        if(!file.is_open())
        {
            std::cout << "config file open failed" << std::endl;
            return false;
        }
        //1.打开文件成功的情况
        //1.1从文件中获取一行信息
        //1.1.1对于每一行信息,还需要获取题号,题目名称,题目难易程度,题目路径
        //1.1.2保存在结构体当中
        //2。把多个question,组织在map当中 
        std::string line;
        while(std::getline(file,line))
        {
            //boost::spilt
            //切割一行的信息
            //切割后的信息存在vec_中
            std::vector<std::string> vec;
            StringUtil::Split(line, "\t", &vec);//按/t分割
            //is_any_of:支持多个字符作为分隔符
            // token_compress_off:是否将多个分隔字符看作是一个
            //boost::split(vec, line, boost::is_any_of(" "), boost::token_compress_off);
            
            Question ques;
            ques.id_ = vec[0];
            ques.title_ = vec[1];
            ques.star_ = vec[2];
            ques.path_ = vec[3];

            //临时变量存储刚刚读回来文件路径
            std::string dir = vec[3];
            FileUtil::ReadFile(dir + "/desc.txt", &ques.desc_);
            FileUtil::ReadFile(dir + "/header.cpp", &ques.header_cpp_);
            FileUtil::ReadFile(dir + "/tail.cpp", &ques.tail_cpp_);

            //将ques保存
            ques_map_[ques.id_] = ques;
        }
        file.close();
        return true;
   }
    //2.提供给上层调用着一个获取所有试题的接口
    bool GetAllQuestion(std::vector<Question>* questions)
    {
        //1.遍历无序的map,将试题信息返回给调用者
        //对于每个试题,都是一个question结构体对象
        for(const auto& kv:ques_map_)
        {
            questions->push_back(kv.second);//无序插入
        }
        //2.想实现顺序的排列,需要排序
        std::sort(questions->begin(), questions->end(),[](const Question& l, const Question& r){
            //比较Question当中的题目编号,按照升序排列
            return std::stoi(l.id_) < std::stoi(r.id_);
            });
        return true;
    }
    //3.提供给上层调用者一个获取单个试题的接口
    //id:输出条件,查找题目的ID
    //ques:输出参数,将查到的结果返回给调用者
    bool GetOneQuestion(const std::string& id, Question* ques)
    {
        auto it = ques_map_.find(id);
        if(it == ques_map_.end())
        {
            return false;
        }
        *ques = it->second;
        return true;
    }
    private:
    std::unordered_map<std::string, Question> ques_map_;//一个题目id,对应一个题目,题目内容用结构体表示
};

编译运行模块

compile.hpp代码

#pragma once
#include <unistd.h>//alarm函数
#include <iostream>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string>
#include <sys/resource.h>
#include <jsoncpp/json/json.h>
#include <atomic> 
//#include <sys/types.h>
#include "tools.hpp"
enum ErrorNo
{
    OK = 0,
    PRAM_ERROR,
    INTERNAL_ERROR,
    COMPILE_ERROR,
    RUN_ERROR
};
class Compiler
{
    public:

    static void CompileAndRun(Json::Value Req, Json::Value* Resp)
    {
        //1.参数是否是错误的,json串当中的code 是否为空
        if(Req["code"].empty())
        {
            (*Resp)["errorno"] = PRAM_ERROR;
            (*Resp)["reason"] = "Pram error";
            return;
        }
        //2.将代码写道文件当中去
        std::string code = Req["code"].asString();
        //文件命名约定的时候,tem_时间戳_src.cpp
        //用来区分不同的请求 
        std::string file_nameheader = WriteTmpFile(code);
        if(file_nameheader == "")
        {
            (*Resp)["errorno"] = INTERNAL_ERROR;
            (*Resp)["reason"] = "write file failed";
            return;
        }
        //3.编译
        if(!Compile(file_nameheader))
        {
            (*Resp)["errorno"] = COMPILE_ERROR;
            std::string reason;
            FileUtil::ReadFile(CompileErrorPath(file_nameheader), &reason);
            (*Resp)["reason"] = reason;
            return;
        }
        //4.运行
        int ret = Run(file_nameheader);
        //-1 -2 大于0
        if(ret != 0)
        {
            (*Resp)["errorno"] = RUN_ERROR;
            (*Resp)["reason"] == "program exit by sig:" + std::to_string(ret);
            return;
        }
        //5.构造响应
        (*Resp)["errorno"] = OK;
        (*Resp)["reason"] = "Compile and Run ok";

        std::string stdout_str;
        FileUtil::ReadFile(StdoutPath(file_nameheader), &stdout_str);
        (*Resp)["stdout"] = stdout_str;

        std::string stderr_str;
        FileUtil::ReadFile(StderrPath(file_nameheader), &stderr_str);
        (*Resp)["stderr"] = stderr_str;
        //6.删除临时文件
         //Clean(file_nameheader);  
         return;
    }
    private:
     //一个删除临时文件的函数
     static void Clean(const std::string& filename)
     {
         unlink(SrcPath(filename).c_str());
         unlink(CompileErrorPath(filename).c_str());
         unlink(ExePath(filename).c_str());
         unlink(StdoutPath(filename).c_str());
         unlink(StderrPath(filename).c_str());
     }
    static int Run(const std::string& file_name)
    {
        //1.创建子进程
        //2.子进程程序替换
        int pid = fork();
        if(pid < 0)
        {
            return -1;
        }
        else if(pid > 0)
        {
            //father
            int status = 0;
            waitpid(pid, &status, 0);
            //终止信号
            return status & 0x7f;
        }
        else
        {
            //注册一个定时器 alarm
            //为的是限制代码,如果运行时间太长了
            alarm(1);
            //child
            //进程程序替换,替换创建编译出来的可执行程序
            //内存限制需要包含头文件#include <sys/resource.h>
            struct rlimit rlim;
            rlim.rlim_cur = 30000 * 1024;
            rlim.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_AS, &rlim);
            int stdout_fd = open(StdoutPath(file_name).c_str(), O_CREAT | O_WRONLY, 0666);
            if(stdout_fd < 0)
            {
                return -2;
            }
            //重定向,将标准输入1重定向到stdin_fd中
            dup2(stdout_fd, 1);
            int stderr_fd = open(StderrPath(file_name).c_str(), O_CREAT | O_WRONLY, 0666);
            if(stderr_fd < 0)
            {
                return -2;
            }
            //重定向,将标准错误1重定向到stderr_fd中
            dup2(stderr_fd, 2);
            execl(ExePath(file_name).c_str(), ExePath(file_name).c_str(), NULL);
            exit(0);
        }
        return 0;
    }
    static bool Compile(const std::string& file_name)
    {
        //1.创建一个子进程
        // 1.1父进程进行进程等待
        //  1.2子进程进行进程程序替换
        //
        int pid = fork();
        if(pid > 0)
        {
            waitpid(pid, NULL, 0);
        }
        else if(pid == 0)
        {
            //child
            //进程程序替换----》g++ SrcPath(filename) -o ExePath(filename) "-std=c++11"
            int fd = open(CompileErrorPath(file_name).c_str(), O_CREAT | O_WRONLY, 0666);
            if(fd < 0)
            {
                return false;
            }
            //将标准错误重定向为fd,标准错误的输出就会输出在文件当中
            dup2(fd, 2);

            execlp("g++", "g++", SrcPath(file_name).c_str(), "-o", ExePath(file_name).c_str(), "-std=c++11", "-D", "CompileOnline", NULL);
            close(fd);
            //如果替换失败了,就直接让子进程退出了, 如果替换成功了,不会走该逻辑 
            exit(0);
        }
        else
        {
            return false;
        }
        //如果编译成功了,在tmp_file这个文件夹下,一定会差生一个可执行程序,
        //如果当前代码走到这里,判断有该可执行程序,则我们认为g++执行成功了, 否则,认为执行失败
        //1.判断是否产生可执行程序, 2.是文件状态,stat是用来判断程序到底有没有生成可执行文件
        //如果stat函数返回0,代表有可执行的程序
        struct stat st;
        int ret = stat(ExePath(file_name).c_str(), &st);
        if(ret < 0)
        {
            return false;
        }
        return true;
    }
    static std::string StdoutPath(const std::string& filename)
    {
        return "./tmp_file/" + filename + ".stdout";
    }
    static std::string StderrPath(const std::string& filename)
    {
        return "./tmp_file/" + filename + ".stderr";
    }
    static std::string CompileErrorPath(const std::string& filename)
    {
        return "./tmp_file/" + filename + ".Compilerr";
    }
    static std::string ExePath(const std::string& filename)
    {
        return "./tmp_file/" + filename + ".executable";
    }
    static std::string SrcPath(const std::string& filename)
    {
        //./tmp_file/ + "tmp_" + std::to_string(TimeUtil::GetTimeStampMs()) + "." + std::to_string(id) + .cpp
        return "./tmp_file/" + filename + ".cpp";
    }

    static std::string WriteTmpFile(const std::string& code)
    {
        //1.组织文件名称,区分源码文件,以及后面生成的可执行程序文件
        static std::atomic_uint id(0);
        //第一部分tem_,第二部分是时间戳,第三部分是一个线程安全的id
        std::string tmp_filename = "tmp_" + std::to_string(TimeUtil::GetTimeStampMs()) + "." + std::to_string(id);
        id++;
        //2.code写到文件当中去
        //tmp_stamp.cpp
        FileUtil::WriteFile(SrcPath(tmp_filename), code);
        return tmp_filename;
    }
};

工具模块

oj_view.hpp代码

//专门用来填充模板文件的
#pragma once
#include <iostream>
#include "ctemplate/template.h"
#include <vector>
#include "oj_model.hpp"
class OjView
{
    public:
    static void DrawAllQuestions(std::vector<Question>& questions, std::string* html)
    {
        //1.创建template 字典的对象
        ctemplate::TemplateDictionary dict("all_questions");
        //2.遍历vector,遍历vector就相当于遍历每一个试题,每一个试题构造一个字典
        //一个大字典里有许多小字典
        for(const auto& ques : questions)
        {
            //TemplateDictionary* AddSectionDictionary(const TemplateString section_name);
            ctemplate::TemplateDictionary* sub_dict = dict.AddSectionDictionary("question");
            //为了往谷歌下的一个模板里填充题目的信息
            //void SetValue(const TemplateString variable, const TemplateString value);
            /*
             *variable:指定的是在预定义html当中的变量名称
             * value:替换的值
             */
            sub_dict->SetValue("id", ques.id_);
            sub_dict->SetValue("id", ques.id_);
            sub_dict->SetValue("title", ques.title_);
            sub_dict->SetValue("star", ques.star_);
        }
        //3.填充
        ctemplate::Template* tl = ctemplate::Template::GetTemplate("./template/all_questions.html", ctemplate::DO_NOT_STRIP);
        //渲染方式   
        tl->Expand(html, &dict);
    }
    static void DrawOneQuestion(const Question& ques, std::string* html)
    {
        ctemplate::TemplateDictionary dict("question");
        dict.SetValue("id", ques.id_);
        dict.SetValue("title", ques.title_);
        dict.SetValue("star", ques.star_);
        dict.SetValue("desc", ques.desc_);
        dict.SetValue("id", ques.id_);
        dict.SetValue("code", ques.header_cpp_);
        ctemplate::Template* tl = ctemplate::Template::GetTemplate("./template/question.html", ctemplate::DO_NOT_STRIP);
             //渲染    
        tl->Expand(html, &dict);
    }
    static void DrawCaseResult(const std::string& err_no, const std::string& q_result, const std::string& reason, std::string* html)
    {
        ctemplate::TemplateDictionary dict("question");
        //填充
        dict.SetValue("errno", err_no);
        dict.SetValue("compile_result", reason);
        dict.SetValue("case_result", q_result);
        //渲染
        ctemplate::Template* tl = ctemplate::Template::GetTemplate("./template/case_result.html", ctemplate::DO_NOT_STRIP);
        tl->Expand(html,&dict);
    }
};

tools.hpp代码

#pragma once
#include <iostream>
#include <string>
#include <fstream>
#include <boost/algorithm/string.hpp> 
#include <vector>
#include <unordered_map>
#include <sys/time.h>
#include <unistd.h>
class FileUtil
{
    public:
    //读文件接口
    //file_name: 文件名称
    //content: 读到的内容,输出参数, 返还给调用者
    static bool ReadFile(const std::string& file_name, std::string* content)
    {
        //1.清空content当中的内容
        content->clear();
        std::ifstream file(file_name.c_str());
        //如果打开失败了
        if(!file.is_open())
        {
            return false;
        }
        //文件被打开了
        std::string line;
        while(std::getline(file, line))
        {
            (*content) += line + "\n";
        }
        file.close();
        return true;
    }
    static bool WriteFile(const std::string& filename, const std::string data)
    {
        std::ofstream file(filename.c_str());
        if(!file.is_open())
        {
            return false;
        }
        file.write(data.c_str(), data.size());
        file.close();
        return true;
    }
};
class StringUtil
{
    public:
    //提供一个切割函数
    static void Split(const std:: string& input, const std::string& split_char, std::vector<std::string>* output)
    {
        boost::split(*output, input, boost::is_any_of(split_char), boost::token_compress_off);
    }
};

class UrlUtil
{
    public:
    //body从httplib.h当中的request类的成员变量获得
    //body
    //key=value&key1=value--->并且是进行过编码
    //1.先切割
    //2.在堆切割之后的结果进行转码
    static void PraseBody(const std::string& body, std::unordered_map<std::string, std::string>* body_kv)
    {
        std::vector<std::string> kv_vec;
        StringUtil::Split(body, "&", &kv_vec);

        for(const auto& t : kv_vec)
        {
            std::vector<std::string> sig_kv;
            StringUtil::Split(t, "=", &sig_kv);

            if(sig_kv.size() != 2)
            {
                continue;
            }
            //在保存的时候,针对value在进行转码
            (*body_kv)[sig_kv[0]] = UrlDecode(sig_kv[1]);
        }
    }
    static unsigned char ToHex(unsigned char x)
    {
        return x > 9 ? x + 55 : x + 48;
    }
    static unsigned char FromHex(unsigned char x)
    {
        unsigned char y;
        if(x >= 'A' && x <= 'Z') y = x - 'A' + 10;
        else if(x >= 'a' && x <= 'z') y = x - 'a' + 10;
        else if(x >= '0' && x <= '9') y = x - '0';
        else assert(0);
        return y;
    }

    static std::string UrlEncode(const std::string& str)
    {
        std::string strTemp = "";
        size_t length = str.length();
        for(size_t i = 0; i < length; i ++)
        {
            if(isalnum((unsigned char)str[i]) || 
                (str[i] == '-') ||
                (str[i] == '_') ||
                (str[i] == '.') ||
                (str[i] == '~'))
                strTemp += str[i];
                else if(str[i] == ' ')
                    strTemp += "+";
                    else
                    {
                        strTemp += '%';
                        strTemp += ToHex((unsigned char)str[i] >> 4);
                        strTemp += ToHex((unsigned char)str[i] % 16);
                    }
        }
        return strTemp;
    }

static std::string UrlDecode(const std::string& str)  
{  
    std::string strTemp = "";  
    size_t length = str.length();  
    for (size_t i = 0; i < length; i++)  
    {  
        if (str[i] == '+') strTemp += ' ';  
        else if (str[i] == '%')  
        {  
            assert(i + 2 < length);  
            unsigned char high = FromHex((unsigned char)str[++i]);  
            unsigned char low = FromHex((unsigned char)str[++i]);  
            strTemp += high*16 + low;  
         }  
         else strTemp += str[i];  
    }  
    return strTemp;  
}
};

//获取时间戳
class TimeUtil
{
    public:
    static int64_t GetTimeStampMs()
    {
        struct timeval tv;
        gettimeofday(&tv, NULL);
        return tv.tv_sec + tv.tv_usec / 1000;
    }
        //[年月日 时分秒]
        //用到time接口,和localtime接口计算的传进来的秒数,返回时分秒
        static void GetTimeStamp(std::string* TimeStamp)
        {
            time_t st;
            time(&st);
            struct tm* t = localtime(&st);
            char buf[30] = { 0 };
            snprintf(buf, sizeof(buf) - 1, "%04d-%02d-%02d %02d:%02d:%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
            TimeStamp->assign(buf, strlen(buf));
         }
 };

enum LogLevel
{
    INFO = 0,
    WARNING,
    ERROR,
    FATAL,
    DEBUG
};
const char* Level[] = 
{
    "INFO",
    "WARNING",
    "ERROR",
    "FATAL",
    "DEBUG"
};
//lev:日志等级
//file:文件名称
//line:行号
//logmsg:想要记录的日志内容 

inline std::ostream& Log(LogLevel lev, const char* file, int line, const std::string& logmsg)
{ 
    std::cout << "begin log" << std::endl;
    std::string level_info = Level[lev];
    std::cout << level_info << std::endl;
    std::string TimeStamp;
    TimeUtil::GetTimeStamp(&TimeStamp);
    std::cout << TimeStamp << std::endl;
    std::cout << "[" << TimeStamp << " " << level_info << " " << file << ":" << line << "]" << " " << logmsg;
    return std::cout;
}

#define LOG(lev, msg) Log(lev, __FILE__, __LINE__, msg)

试题的配置文件

oj_data

1.文件夹1

1.desc.txt

判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

示例 1:

输入: 121
输出: true
示例 2:

输入: -121
输出: false
解释: 从左向右读,-121 。 从右向左读,121- 。因此它不是一个回文数。
示例 3:

输入: 10
输出: false
解释: 从右向左读,01 。因此它不是一个回文数。
进阶:

你能不将整数转为字符串来解决这个问题吗?

2.header.cpp


#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;

class Solution {
  public:
    bool isPalindrome(int x) {
      return true;         
    }
};

3.tail.cpp

#ifndef CompileOnline
// 这是为了编写用例的时候有语法提示. 实际线上编译的过程中这个操作是不生效的.
#include "header.cpp"
#endif

///
// 此处约定:
// 1. 每个用例是一个函数
// 2. 每个用例从标准输出输出一行日志
// 3. 如果用例通过, 统一打印 [TestName] ok!
// 4. 如果用例不通过, 统一打印 [TestName] failed! 并且给出合适的提示.
///

void Test1() {
  bool ret = Solution().isPalindrome(121);
  if (ret) {
    std::cout << "Test1 ok!" << std::endl;
  } else {
    std::cout << "Test1 failed! input: 121, output expected true, actual false" << std::endl;
  }
}

void Test2() {
  bool ret = Solution().isPalindrome(-10);
  if (!ret) {
    std::cout << "Test2 ok!" << std::endl;
  } else {
    std::cout << "Test2 failed! input: -10, output expected false, actual true" << std::endl;
  }
}

int main() {
  Test1();
  Test2();
  return 0;
}

2.oj_config.cfg

1	回文数	简单	./oj_data/1
2	单链表	简单	./oj_data/2
3	回文数	简单	./oj_data/1
4	回文数	简单	./oj_data/1
5	回文数	简单	./oj_data/1
6	回文数	简单	./oj_data/1
7	回文数	简单	./oj_data/1
8	回文数	简单	./oj_data/1
9	回文数	简单	./oj_data/1
10	回文数	简单	./oj_data/1

填充的html源码

template

1.all_questions.html

<html>
<head>
  <meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>
<body>
  {{#question}}
  <div>
    <a href="/question/{{id}}">{{id}}.{{title}}({{star}})</a>
  </div>
  {{/question}}
</body>
</html>

2.question.html

<html>
<head>
  <meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>
<body>
  <div>{{id}}.{{title}}({{star}})</div>
  <div><pre>{{desc}}</pre></div>
  <div>
    <form action="/compile/{{id}}" method="post">
      <textarea name="code" rows=30 cols=100>{{code}}</textarea>
      <br>
      <textarea name="stdin" rows=30 cols=100>hello</textarea>
      <input type="submit" value="Submit">
    </form>
  </div>
</body>
</html>

3.case_result.html

<html>
<head>
  <meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>
<body>
  <div><pre>error_no : {{errno}}</pre></div>
  <div><pre>{{compile_result}}</pre></div>
  <div><pre>{{case_result}}</pre></div>
</body>
</html>

makefile

INCLUDE_PATH=-I /home/limengru/third_part/include -I /usr/include/jsoncpp
LIB_PATH=-L /home/limengru/third_part/lib -lctemplate -ljsoncpp -lpthread

BIN=../bin/oj_svr
$(BIN):oj_server.cpp
	g++ $^ -o $@ -g $(INCLUDE_PATH) $(LIB_PATH)

.PHONY:clean
clean:
	rm $(BIN)
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-04 11:50:13  更:2022-04-04 11:54:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:41:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码