当前位置: 首页 > news >正文

『C++实战项目 负载均衡式在线OJ』二、编译模块编写(持续更新)

文章目录

  • 一、新建文件 compiler.hpp
  • 二、代码编写
      • Compile 函数实现
  • 实用工具类实现
    • 一、新建文件 util.hpp
    • 二、代码编写
  • 本节完整源码
    • 目录结构
    • 源码
      • compiler.hpp
      • util.hpp

我们首先来编写 complier 模块。compiler 模块的主要功能是使用 g++ 完成对指定文件的编译。

该模块我们将用 fork 创建子进程 + fcnt 程序替换来完成。由于我已经在之前的文章中讲解过子进程和程序替换的相关知识了,所以在此不做赘述。需要的小伙伴可以点这里:

  • fork 相关:『Linux从入门到精通』第 ⑬ 期 - 进程概念与fork初识;
  • 程序替换相关:『Linux从入门到精通』第 ⑱ 期 - 学会了程序替换,我决定手写一个简易版shell玩一玩…

接下来进入代码的编写。

一、新建文件 compiler.hpp

进入项目目录,在 compiler 目录下新建 compiler.hpp 文件。目录结构:

online-judge -------- compiler -------- compiler.hpp

二、代码编写

  1. 定义 compiler 类,该类包含一个静态成员函数 Compile
  2. 该函数 Compile 声明为 static,使得我们无需创建对象就能调用该函数;

Compile 函数实现

  1. 创建子进程;
// filename: 需要编译的目标文件 不包含文件后缀(如 main)
static bool Compile(const std::string &filename) {pid_t pid = fork();if(pid < 0) {ERROR("compiler: 创建子进程失败");return false;}else if(pid == 0) {// 子进程// ...}else {// 父进程// ...}return false;
}
  1. 实现子进程功能;

子进程主要负责程序替换来执行 g++ 进行编译。注意下面的代码中使用了文件工具类中的几个函数,但是我们目前还没有实现它们。在这里我们只需要知道它们的功能即可。函数实现放在后文。

  • Exe: 为文件名添加后缀 .exe。代表我们生成的可执行文件名为 filename.exe;
  • Src: 为文件名添加后缀 .cpp。代表我们编译的源文件为 filename.cpp;
  • ERROR: 我的日志系统项目中实现的函数 ERROR。原理同 std::cerr << "Error: 启动编译器失败" << std::endl;
// filename: 需要编译的目标文件 不包含文件后缀(如 main)
static bool Compile(const std::string &filename) {pid_t pid = fork();if(pid < 0) {ERROR("compiler: 创建子进程失败");return false;}else if(pid == 0) {// 子进程// 子进程:调用编译器,完成对代码的编译工作// g++ -o target src -lpthread -std=c++11execlp("g++", "g++", "-o", PathUtil::Exe(filename).c_str(), \PathUtil::Src(filename).c_str(), "-D", "COMPILER_ONLINE", "-std=c++11", nullptr);ERROR("compiler: 启动编译器失败");exit(2);}else {// 父进程// ...}return false;
}
  1. 实现父进程功能;

父进程主要负责等待子进程,检查子进程是否成功完成任务。

  • IsFileExists: 判断指定文件是否存在的函数;
// filename: 需要编译的目标文件 不包含文件后缀(如 main)
static bool Compile(const std::string &filename) {pid_t pid = fork();if(pid < 0) {ERROR("compiler: 创建子进程失败");return false;}else if(pid == 0) {// 子进程// 子进程:调用编译器,完成对代码的编译工作// g++ -o target src -lpthread -std=c++11execlp("g++", "g++", "-o", PathUtil::Exe(filename).c_str(), \PathUtil::Src(filename).c_str(), "-D", "COMPILER_ONLINE", "-std=c++11", nullptr);ERROR("compiler: 启动编译器失败");exit(2);}else {// 父进程// 等待子进程waitpid(pid, nullptr, 0);// 判断编译是否成功:是否生成对应的可执行程序文件if(FileUtil::IsFileExists(PathUtil::Exe(filename))) {INFO("compiler: 编译成功");return true;}ERROR("compiler: 编译失败, 未找到可执行程序");}return false;
}

实用工具类实现

在上文中,我们用到了许多还没有实现的工具函数。这些函数往往具有简单的功能,且实现起来也很简单,并且使用率较高。所以我们放在一个文件中统一来实现它们。

一、新建文件 util.hpp

  1. 创建 comm 目录和 util.hpp 文件。
# online-judge目录下
mkdir comm
touch util.hpp

二、代码编写

由于这些函数实现起来非常简单,这里就不做赘述了。

#pragma once#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>namespace ns_util {
class FileUtil {
public:static bool IsFileExists(const std::string & filepath) {struct stat st;if(stat(filepath.c_str(), &st) == 0) {// 获取文件属性成功,证明文件已经存在return true;}return false;}
class PathUtil {
public:static std::string AddSuffix(const std::string &filename, const std::string &suffix) {std::string path = temp_path;path += filename;path += suffix;return path;} static std::string CompilerErr(const std::string & filename) {return AddSuffix(filename, ".compile_err");}static std::string Src(const std::string & filename) {return AddSuffix(filename, ".cpp");}static std::string Exe(const std::string & filename) {return AddSuffix(filename, ".exe");}// 在线测试用户提交的代码时,有时用到标准输入、标准输出、标准错误流.// 这里分别用 .in, .out, .err 文件存储起来static std::string In(const std::string & filename) {return AddSuffix(filename, ".in");}static std::string Out(const std::string & filename) {return AddSuffix(filename, ".out");}static std::string Err(const std::string & filename) {return AddSuffix(filename, ".err");}
};
}

本节完整源码

目录结构

online-judge -------- compiler -------- compiler.hpp|--------- comm ------------ util.hpp

源码

compiler.hpp

#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "../log/logs/log.h"
#include "../comm/util.hpp"/*********************************** 编译模块** 使用子进程 + 程序替换实现
**********************************/
namespace ns_compiler {using namespace ns_util;
using namespace LOG;class Compiler {
public:Compiler() {}~Compiler() {}
public:// filename: 需要编译的目标文件 不包含文件后缀(如 main)static bool Compile(const std::string &filename) {pid_t pid = fork();if(pid < 0) {ERROR("compiler: 创建子进程失败");return false;}else if(pid == 0) {// 子进程// 程序替换 不影响进程的文件描述符表// 子进程:调用编译器,完成对代码的编译工作// g++ -o target src -lpthread -std=c++11execlp("g++", "g++", "-o", PathUtil::Exe(filename).c_str(), \PathUtil::Src(filename).c_str(), "-D", "COMPILER_ONLINE", "-std=c++11", nullptr);ERROR("compiler: 启动编译器失败");exit(2);}else {// 父进程waitpid(pid, nullptr, 0);// 判断编译是否成功:是否生成对应的可执行程序文件if(FileUtil::IsFileExists(PathUtil::Exe(filename))) {INFO("compiler: 编译成功");return true;}ERROR("compiler: 编译失败, 未找到可执行程序");}return false;}
};
}

util.hpp

#pragma once#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>namespace ns_util {
class FileUtil {
public:static bool IsFileExists(const std::string & filepath) {struct stat st;if(stat(filepath.c_str(), &st) == 0) {// 获取文件属性成功,证明文件已经存在return true;}return false;}
class PathUtil {
public:static std::string AddSuffix(const std::string &filename, const std::string &suffix) {std::string path = temp_path;path += filename;path += suffix;return path;} static std::string CompilerErr(const std::string & filename) {return AddSuffix(filename, ".compile_err");}static std::string Src(const std::string & filename) {return AddSuffix(filename, ".cpp");}static std::string Exe(const std::string & filename) {return AddSuffix(filename, ".exe");}// 在线测试用户提交的代码时,有时用到标准输入、标准输出、标准错误流.// 这里分别用 .in, .out, .err 文件存储起来static std::string In(const std::string & filename) {return AddSuffix(filename, ".in");}static std::string Out(const std::string & filename) {return AddSuffix(filename, ".out");}static std::string Err(const std::string & filename) {return AddSuffix(filename, ".err");}
};
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【前端 22】使用Nginx部署前端项目
  • 手持式气象站:科技赋能精准气象观测
  • 长短期记忆网络LSTM
  • 自制安卓车机软件(含APP)
  • 1、爬⾍概述
  • 【2024】Datawhale AI夏令营 Task4笔记——vllm加速方式修改及llm推理参数调整上分
  • 神经网络基础--激活函数
  • 深拷贝——JSON.stringify()序列化和JSON.prase()反序列化
  • 【C语言】Top K问题【建小堆】
  • 浙大版《C语言程序设计(第3版)》题目集
  • JavaScript 继承百花齐放:从原型链到 ES6 类
  • 软设之TCP/IP协议
  • 软科中国大学排名爬虫+数据可视化
  • 图片管理组建
  • Flink 实时数仓(三)【DWD 层搭建(一)】
  • (ckeditor+ckfinder用法)Jquery,js获取ckeditor值
  • 08.Android之View事件问题
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • httpie使用详解
  • IndexedDB
  • Java比较器对数组,集合排序
  • Js基础知识(四) - js运行原理与机制
  • Python3爬取英雄联盟英雄皮肤大图
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • vue.js框架原理浅析
  • webpack4 一点通
  • 阿里云前端周刊 - 第 26 期
  • 初探 Vue 生命周期和钩子函数
  • 道格拉斯-普克 抽稀算法 附javascript实现
  • 复杂数据处理
  • 更好理解的面向对象的Javascript 1 —— 动态类型和多态
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 智能合约Solidity教程-事件和日志(一)
  • 自定义函数
  • elasticsearch-head插件安装
  • 正则表达式-基础知识Review
  • 昨天1024程序员节,我故意写了个死循环~
  • ​一、什么是射频识别?二、射频识别系统组成及工作原理三、射频识别系统分类四、RFID与物联网​
  • ‌前端列表展示1000条大量数据时,后端通常需要进行一定的处理。‌
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • #70结构体案例1(导师,学生,成绩)
  • #etcd#安装时出错
  • #图像处理
  • $(this) 和 this 关键字在 jQuery 中有何不同?
  • (3)Dubbo启动时qos-server can not bind localhost22222错误解决
  • (9)STL算法之逆转旋转
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (分享)一个图片添加水印的小demo的页面,可自定义样式
  • (三)Kafka 监控之 Streams 监控(Streams Monitoring)和其他
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会
  • (图)IntelliTrace Tools 跟踪云端程序
  • (一)Mocha源码阅读: 项目结构及命令行启动