图示
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;
Server svr;
svr.Get("/all_questions",[&model](const Request& rep, Response& resp){
std::vector<Question> questions;
model.GetAllQuestion(&questions);
std::string html;
OjView::DrawAllQuestions(questions, &html);
resp.set_content(html, "text/html");
});
svr.Get(R"(/question/(\d+))", [&model](const Request& req, Response& resp){
std::cout << req.version << " " << req.method << std::endl;
std::cout << req.path << std::endl;
Question ques;
model.GetOneQuestion(req.matches[1].str(), &ques);
std::string html;
OjView::DrawOneQuestion(ques, &html);
resp.set_content(html, "text/html");
});
svr.Post(R"(/compile/(\d+))", [&model](const Request& req, Response& resp){
Question ques; model.GetOneQuestion(req.matches[1].str(), &ques);
std::unordered_map<std::string, std::string> body_kv;
UrlUtil::PraseBody(req.body, &body_kv);
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;
Json::Value resp_json;
Compiler::CompileAndRun(req_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 "tools.hpp"
#include <vector>
#include <unordered_map>
struct Question{
std::string 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()
{
}
bool Load(const std::string& filename)
{
std::ifstream file(filename.c_str());
if(!file.is_open())
{
std::cout << "config file open failed" << std::endl;
return false;
}
std::string line;
while(std::getline(file,line))
{
std::vector<std::string> vec;
StringUtil::Split(line, "\t", &vec);
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_map_[ques.id_] = ques;
}
file.close();
return true;
}
bool GetAllQuestion(std::vector<Question>* questions)
{
for(const auto& kv:ques_map_)
{
questions->push_back(kv.second);
}
std::sort(questions->begin(), questions->end(),[](const Question& l, const Question& r){
return std::stoi(l.id_) < std::stoi(r.id_);
});
return true;
}
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_;
};
编译运行模块
compile.hpp代码
#pragma once
#include <unistd.h>
#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 "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)
{
if(Req["code"].empty())
{
(*Resp)["errorno"] = PRAM_ERROR;
(*Resp)["reason"] = "Pram error";
return;
}
std::string code = Req["code"].asString();
std::string file_nameheader = WriteTmpFile(code);
if(file_nameheader == "")
{
(*Resp)["errorno"] = INTERNAL_ERROR;
(*Resp)["reason"] = "write file failed";
return;
}
if(!Compile(file_nameheader))
{
(*Resp)["errorno"] = COMPILE_ERROR;
std::string reason;
FileUtil::ReadFile(CompileErrorPath(file_nameheader), &reason);
(*Resp)["reason"] = reason;
return;
}
int ret = Run(file_nameheader);
if(ret != 0)
{
(*Resp)["errorno"] = RUN_ERROR;
(*Resp)["reason"] == "program exit by sig:" + std::to_string(ret);
return;
}
(*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;
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)
{
int pid = fork();
if(pid < 0)
{
return -1;
}
else if(pid > 0)
{
int status = 0;
waitpid(pid, &status, 0);
return status & 0x7f;
}
else
{
alarm(1);
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;
}
dup2(stdout_fd, 1);
int stderr_fd = open(StderrPath(file_name).c_str(), O_CREAT | O_WRONLY, 0666);
if(stderr_fd < 0)
{
return -2;
}
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)
{
int pid = fork();
if(pid > 0)
{
waitpid(pid, NULL, 0);
}
else if(pid == 0)
{
int fd = open(CompileErrorPath(file_name).c_str(), O_CREAT | O_WRONLY, 0666);
if(fd < 0)
{
return false;
}
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;
}
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)
{
return "./tmp_file/" + filename + ".cpp";
}
static std::string WriteTmpFile(const std::string& code)
{
static std::atomic_uint id(0);
std::string tmp_filename = "tmp_" + std::to_string(TimeUtil::GetTimeStampMs()) + "." + std::to_string(id);
id++;
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)
{
ctemplate::TemplateDictionary dict("all_questions");
for(const auto& ques : questions)
{
ctemplate::TemplateDictionary* sub_dict = dict.AddSectionDictionary("question");
sub_dict->SetValue("id", ques.id_);
sub_dict->SetValue("id", ques.id_);
sub_dict->SetValue("title", ques.title_);
sub_dict->SetValue("star", ques.star_);
}
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:
static bool ReadFile(const std::string& file_name, std::string* 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:
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;
}
(*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;
}
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"
};
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
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)
|