【C++项目】从零实现一个在线编译器

在这里插入图片描述

前言

身为一名程序员,想必大家都有接触过像leetcode这样的刷题网站,不知你们在刷题的过程中是否思考过一个问题:它们是如何实现在线编译运行的功能。如果你对此感到好奇,那么本文将一步步带你来实现一个简易在线编译器。

项目概述

项目的基本逻辑:前端用户在网页上输入代码与参数,后端通过多进程的方式来编译运行代码,然后将标准输出、标准错误信息返回给前端页面。

前后端交互数据格式

// 前端发送
{"code": "代码","cpu_limit": "CPU限制","mem_limit": "内存限制"
}// 后端发送
{"reason": "错误原因","status": "状态码","stderr": "错误输出","stdout": "标准输出"
}

使用的第三方库

后端:

  • cpp-httplib:用于处理HTTP请求和响应。

  • jsoncpp:用于解析和生成JSON数据。

  • spdlog:用于日志记录。

前端:

  • jquery:简化JavaScript操作,方便进行DOM操作和Ajax请求。

  • ace:提供代码编辑器功能,支持语法高亮和代码自动完成。

运行效果

具体实现

后端逻辑

后端分为编译模块和运行模块,均使用多进程的方式来运行,并根据用户所选语言的语言来选择不同的编译器和运行方式。后端代码分为四部分:公共模块(工具类)、编译模块、运行模块、编译运行模块(整合编译与运行)。

公共模块

日记类

日记系统对spdlog进行了最低程度的封装,实现了单例日记系统,并定义宏来简化其使用。

//
// Created by lang liu on 24-4-23.
//#pragma once#ifdef DEBUG
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
#endif#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>namespace ns_log {//TODO 初始化日记 完善class Log {public:static Log &getInstance() {std::call_once(_flag, []() {_instance = new Log();});return *_instance;}auto getLogger()->std::shared_ptr<spdlog::logger>{return _logger;}private:Log() {_logger = spdlog::stdout_color_mt("nil");_logger->set_level(spdlog::level::debug);_logger->set_pattern("[%^%l%$] [%Y-%m-%d %H:%M:%S] [%t] [%s:%#] %v");}~Log() {spdlog::drop_all();}private:static std::once_flag _flag;static Log *_instance;std::shared_ptr<spdlog::logger> _logger;};std::once_flag Log::_flag;Log *Log::_instance = nullptr;#define LOG_DEBUG(...)    SPDLOG_LOGGER_DEBUG(Log::getInstance().getLogger(), __VA_ARGS__)
#define LOG_INFO(...)     SPDLOG_LOGGER_INFO(Log::getInstance().getLogger(), __VA_ARGS__)
#define LOG_WARN(...)     SPDLOG_LOGGER_WARN(Log::getInstance().getLogger(), __VA_ARGS__)
#define LOG_ERROR(...)    SPDLOG_LOGGER_ERROR(Log::getInstance().getLogger(), __VA_ARGS__)
#define LOG_CRITICAL(...) SPDLOG_LOGGER_CRITICAL(Log::getInstance().getLogger(), __VA_ARGS__)
}// #else
//[x] 无spdlog
// #include <iostream>
// #include <format>
// #include "util.hpp"// namespace ns_log {//     using namespace ns_util;//     enum {
//         INFO,
//         DEBUG,
//         WARN,
//         ERROR,
//         CRITICAL
//     };//     inline std::ostream &Log(const std::string &level, const std::string &str) {
//         std::string msg = std::format("[{}] [{}] [{}:{}] {}", level, TimeUtil::GetTimeStamp(), __FILE__, __LINE__,
//                                       str);
//         // auto ret = __FILE_NAME__;
//         return std::cout << msg;
//     }// #define LOG_INFO(...)     Log("INFO", __VA_ARGS__)
// #define LOG_DEBUG(...)    Log("DEBUG", __VA_ARGS__)
// #define LOG_WARN(...)     Log("WARN", __VA_ARGS__)
// #define LOG_ERROR(...)    Log("ERROR", __VA_ARGS__)
// #define LOG_CRITICAL(...) Log("CRITICAL", __VA_ARGS__)// }// #endif

工具类

工具类分为时间工具、文件工具、路径工具。

时间工具:时间工具类用于生成时间戳,辅助文件工具生成唯一的文件名(UUID)。

文件工具:用于实现读写、创建、删除文件。

路径工具:用于更改文件的后缀。

//
// Created by lang liu on 24-4-23.
//#ifndef OJ_UTIL_HPP
#define OJ_UTIL_HPP#include "log.hpp"
#include <sys/time.h>
#include <sys/stat.h>
#include <fstream>
#include <atomic>
#include <unordered_map>
#include <filesystem>
#include <vector>namespace ns_util
{using namespace ns_log;// 定义文件后缀名的映射表static inline std::unordered_map<std::string, std::string> suffixTable {{"c_cpp", ".cc"},{"csharp", ".cs"},{"python", ".py"},{"javascript", ".js"}};// 定义可执行文件后缀名的映射表static inline std::unordered_map<std::string, std::string> excuteTable {{"c_cpp", ".exe"},{"csharp", ".cs"},{"javascript", ".js"},{"python", ".py"}};// 时间工具类class TimeUtil{public:// 获取时间戳(秒)static std::string GetTimeStamp(){struct timeval _tv{};gettimeofday(&_tv, nullptr);    // 获取时间戳return std::to_string(_tv.tv_sec);}// 获取时间戳(毫秒),用于生成随机文件名static std::string GetTimeNs(){struct timeval _tv{};gettimeofday(&_tv, nullptr);return std::to_string(_tv.tv_sec * 1000 + _tv.tv_usec / 1000);}};// 文件路径和后缀名变量static std::string path;static std::string srcSuffix;static std::string excuteSuffix;static const std::string temp_path = "./template/";// 路径工具类class PathUtil{public:// 初始化模板路径和后缀名static void InitTemplate(const std::string& lang) {path = temp_path + lang + "/";srcSuffix = suffixTable.at(lang);excuteSuffix = excuteTable.at(lang);}// 添加后缀名到文件名static std::string AddSuffix(const std::string &file_name, const std::string& suffix){std::string path_name = path;path_name += file_name;path_name += suffix;return path_name;}// 获取源文件路径static std::string Src(const std::string& file_name){return AddSuffix(file_name, srcSuffix);}// 获取可执行文件路径static std::string Exe(const std::string& file_name){return AddSuffix(file_name, excuteSuffix);}// 获取编译错误文件路径static std::string CompilerError(const std::string& file_name){return AddSuffix(file_name, ".compile_error");}// 获取标准输入文件路径static std::string Stdin(const std::string& file_name){return AddSuffix(file_name, ".stdin");}// 获取标准输出文件路径static std::string Stdout(const std::string& file_name){return AddSuffix(file_name, ".stdout");}// 获取标准错误文件路径static std::string Stderr(const std::string& file_name){return AddSuffix(file_name, ".stderr");}};// 文件工具类class FileUtil{public:// 移除文件夹中的所有文件(递归)static void RemoveAllFile(const std::string& dir) {std::vector<std::filesystem::path> removeArray;try {for(auto& it : std::filesystem::directory_iterator(dir)) {if(std::filesystem::is_regular_file(it.path()))removeArray.push_back(it.path());else if (std::filesystem::is_directory(it.path()))RemoveAllFile(it.path().string());}for(auto& it : removeArray) {std::filesystem::remove(it);}}catch (const std::filesystem::filesystem_error& e) {LOG_ERROR("移除全部文件失败: {}", e.what());}}// 移除指定文件static void RemoveFile(const std::string& filename) {if(std::filesystem::exists(filename)) {std::filesystem::remove(filename);}}// 检查文件是否存在static bool IsFileExists(const std::string & path_name){struct stat st = {};if(stat(path_name.c_str(), &st) == 0)return true;return false;}// 生成全局唯一的文件名static std::string UniqFileName(){static std::atomic_uint id(0);++id;std::string ms = TimeUtil::GetTimeNs();std::string uniq_id = std::to_string(id);return ms + uniq_id;}// 将内容写入文件static bool WriteFile(const std::string & path_name, const std::string & content){std::ofstream out(path_name);if(!out.is_open()){LOG_ERROR("write file failed!");return false;}out.write(content.c_str(), (int)content.size());out.close();return true;}// 读取文件内容static bool ReadFile(const std::string& path_name, std::string* content){content->clear();std::ifstream in(path_name);if(!in.is_open()){LOG_ERROR("read file({}) failed!", path_name.c_str());return false;}in.seekg(0, std::ios::end);auto size = in.tellg(); // 获取文件大小in.seekg(0, std::ios::beg);content->resize(size);in.read(content->data(), size);return true;}};};#endif //OJ_UTIL_HPP

编译模块

编译模块因为其大部分代码都是相似的,所以这里将其具体执行逻辑分离开来,并使用工厂模式决定实例化不同的类。

编译模块

#pragma once#include <unistd.h>
#include <sys/fcntl.h>#include "log.hpp"
#include "util.hpp"namespace ns_compiler {using namespace ns_log;using namespace ns_util;// 编译器基类class Compiler {public:virtual ~Compiler() = default;// 编译函数,接受文件名作为参数int Compile(const std::string& file_name) {// 打开标准错误和标准输出文件int _stderr = open(PathUtil::Stderr(file_name).c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644);int _stdout = open(PathUtil::Stdout(file_name).c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644);// 检查文件是否成功打开if(_stderr < 0 || _stdout < 0) {LOG_ERROR("打开文件失败");return -1;}// 创建子进程进行编译pid_t pid = fork();if(pid < 0 ) {LOG_ERROR("线程创建失败: {}", strerror(errno));return -1;}else if(pid == 0) {// 子进程umask(0); // 重置文件掩码// 重定向标准错误和标准输出到文件dup2(_stderr, STDERR_FILENO);dup2(_stdout, STDOUT_FILENO);// 执行编译命令Execute(file_name);LOG_INFO("exec错误:{}", strerror(errno));return -1; // exec 出错}// 父进程等待子进程结束waitpid(pid, nullptr, 0);// 检查编译后的可执行文件是否存在if(!FileUtil::IsFileExists(PathUtil::Exe(file_name))) {LOG_INFO("文件编译失败");return -2;}return 0; // 编译成功}protected:// 执行具体编译命令的纯虚函数,需由子类实现virtual void Execute(const std::string& filename) = 0;};// C++ 编译器类,继承自 Compilerclass CppCompiler : public Compiler {protected:// 执行 g++ 编译命令void Execute(const std::string& filename) override {execlp("g++", "g++", "-o", PathUtil::Exe(filename).c_str(),PathUtil::Src(filename).c_str(), nullptr);}};// 编译器工厂类,用于创建编译器对象class CompilerFactory {public:// 根据语言创建相应的编译器对象static std::unique_ptr<Compiler> CreateCompiler(const std::string& lang) {if(lang == "c_cpp")return std::make_unique<CppCompiler>();// 如果需要支持其他语言,可以在这里添加相应的编译器创建逻辑elsereturn {};}};
}

运行模块

运行模块与编译模块的逻辑其实相差不大。

#pragma once#include "compiler.hpp"
#include <iostream>
#include <sys/resource.h>
#include <sys/wait.h>using namespace ns_log;
using namespace ns_util;// Runner 基类,负责运行已编译的程序
class Runner {
public:// 运行指定的程序文件,并限制其 CPU 和内存使用int Run(const std::string& filename, const int cpu_limit, const int mem_limit) {// 获取输入、输出和错误文件路径std::string _execute = PathUtil::Exe(filename);std::string _stdin = PathUtil::Stdin(filename);std::string _stdout = PathUtil::Stdout(filename);std::string _stderr = PathUtil::Stderr(filename);umask(0); // 重置文件掩码// 打开标准输入、输出和错误文件int stdin_fd = open(_stdin.c_str(), O_CREAT | O_WRONLY, 0644);int stdout_fd = open(_stdout.c_str(), O_CREAT | O_WRONLY, 0644);int stderr_fd = open(_stderr.c_str(), O_CREAT | O_WRONLY, 0644);// 创建子进程以执行程序pid_t pid = fork();if(pid < 0) {LOG_ERROR("fork error");close(stdin_fd);close(stdout_fd);close(stderr_fd);return -1;}else if(pid == 0) {// 子进程dup2(stdin_fd, STDIN_FILENO); // 重定向标准输入到文件dup2(stdout_fd, STDOUT_FILENO); // 重定向标准输出到文件dup2(stderr_fd, STDERR_FILENO); // 重定向标准错误到文件signal(SIGXCPU, [](int sig) { // 处理超时信号std::cerr << "timeout" << std::endl;});SetProcLimit(mem_limit, cpu_limit); // 设置进程资源限制Excute(_execute); // 执行具体的程序LOG_ERROR("execute error");exit(-1); // exec 出错}// 父进程等待子进程完成int status;waitpid(pid, &status, 0);close(stderr_fd);close(stdout_fd);close(stdin_fd);return WEXITSTATUS(status); // 返回子进程的退出状态}virtual ~Runner() = default;protected:// 纯虚函数,由子类实现具体的执行逻辑virtual void Excute(const std::string& filename) = 0;// 设置进程的内存和 CPU 使用限制static void SetProcLimit(const int m_limit, const int c_limit) {rlimit memLimit{};memLimit.rlim_cur = m_limit * 1024; // 内存限制(KB 转换为字节)memLimit.rlim_max = RLIM_INFINITY; // 最大内存限制setrlimit(RLIMIT_AS, &memLimit);rlimit cpuLimit{};cpuLimit.rlim_cur = c_limit; // CPU 时间限制(秒)cpuLimit.rlim_max = RLIM_INFINITY; // 最大 CPU 时间限制setrlimit(RLIMIT_CPU, &cpuLimit);}
};// C++ 运行器类,继承自 Runner
class CppRunner : public Runner {
protected:// 使用 execlp 执行编译后的 C++ 程序void Excute(const std::string& exec) override {execlp(exec.c_str(), exec.c_str(), nullptr);}
};// C# 运行器类,继承自 Runner
class CsharpRunner : public Runner {
protected:// 使用 execlp 执行 C# 程序void Excute(const std::string &filename) override {execlp("dotnet", "dotnet", "run",  filename.c_str(), "--project", path.c_str(), nullptr);}
};// JavaScript 运行器类,继承自 Runner
class JsRunner : public Runner {
protected:// 使用 execlp 执行 JavaScript 程序void Excute(const std::string &filename) override {execlp("node", "node", filename.c_str(), nullptr);}
};// Python 运行器类,继承自 Runner
class PyRunner : public Runner {
protected:// 使用 execlp 执行 Python 程序void Excute(const std::string &filename) override {execlp("python3", "python3", filename.c_str(), nullptr);}
};// 运行器工厂类,用于创建运行器对象
class RunnerFactory {
public:// 根据语言创建相应的运行器对象static std::unique_ptr<Runner> CreateRunner(const std::string& language) {if(language == "c_cpp")return std::make_unique<CppRunner>();else if (language == "csharp")return std::make_unique<CsharpRunner>();else if(language == "python")return std::make_unique<PyRunner>();elsereturn std::make_unique<JsRunner>();}
};

编译运行模块

编译运行模块是编译模块与运行模块的整合,用于解析前端发送的信息,并编译运行,最后将结果返回给前端网页。

#pragma once#include "runner.hpp"
#include "compiler.hpp"
#include "util.hpp"
#include <json/json.h>using namespace ns_log;
using namespace ns_util;
using namespace ns_compiler;// json 输入格式
// code: 代码
// language: 编译器
// cpu_limit: CPU 限制
// mem_limit: 内存限制// json 输出格式
// reason: 错误原因
// status: 状态码
// stderr: 错误输出
// stdout: 标准输出class CompileAndRun
{
private:// 将状态码转换为描述信息static std::string codeToDesc(int status) {static std::unordered_map<int, std::string> errTable = {{-1, "未知错误"},{-2, "编译错误"},{0, "运行成功"},// 更多状态码描述};return errTable[status];}public:// 编译并运行static void Start(std::string& in_json, std::string* out_json){// 转换 JSON 格式Json::Value in_value;Json::Value out_value;Json::Reader reader;if (!reader.parse(in_json, in_value)){LOG_ERROR("CompileAndRun::Start parse json error");return;}// 记录输入的 JSON 数据Json::StyledWriter styledWriter;LOG_DEBUG("in_value: {}", styledWriter.write(in_value));// 从 JSON 中提取参数std::string code = in_value["code"].asString();int cpu_limit = in_value["cpu_limit"].asInt();int mem_limit = in_value["mem_limit"].asInt();std::string lang = in_value["language"].asString();int status_code = 0;// 初始化模板路径和后缀名PathUtil::InitTemplate(lang);std::string file_name = FileUtil::UniqFileName(); // 生成唯一文件名// 创建编译器和运行器对象auto Cp = CompilerFactory::CreateCompiler(lang);auto Rn = RunnerFactory::CreateRunner(lang);if(code.empty()){// 如果代码为空,记录错误并设置状态码LOG_ERROR("CompileAndRun::Start code is empty");status_code = -1;goto END;}// 将代码写入文件if(!FileUtil::WriteFile(PathUtil::Src(file_name), code)){status_code = -1; // 其他错误goto END;}// 编译代码if(Cp){if(Cp->Compile(file_name) < 0) {status_code = -2; // 编译错误goto END;}}// 运行编译后的程序status_code = Rn->Run(file_name, cpu_limit, mem_limit);if(status_code < 0){status_code = -1; // 未知错误goto END;}else if(status_code > 0){goto END;}END:// 构建输出 JSON 数据out_value["status"] = status_code;out_value["reason"] = codeToDesc(status_code);if(status_code == 0){std::string _stdout;FileUtil::ReadFile(PathUtil::Stdout(file_name), &_stdout); // 读取标准输出out_value["stdout"] = _stdout;}else{std::string _stderr;FileUtil::ReadFile(PathUtil::Stderr(file_name), &_stderr); // 读取错误输出out_value["stderr"] = _stderr;}Json::StyledWriter writer;*out_json = writer.write(out_value); // 将结果写入输出 JSONLOG_DEBUG("out_json: {}", *out_json);// 清理生成的文件RemoveFile(file_name);}private:// 移除生成的文件static void RemoveFile(const std::string& filename) {auto filepath = path + filename;auto filesrc = filepath + srcSuffix;auto fileexe = filepath + excuteSuffix;auto fileout = PathUtil::Stderr(filename);auto filein = PathUtil::Stdin(filename);auto fileerr = PathUtil::Stdout(filename);FileUtil::RemoveFile(filesrc);FileUtil::RemoveFile(fileexe);FileUtil::RemoveFile(fileout);FileUtil::RemoveFile(filein);FileUtil::RemoveFile(fileerr);}
};

程序入口

#include "compile_run.hpp"
#include <httplib.h>int main()
{using namespace httplib;using namespace ns_util;// system("pwd");Server svr;svr.set_base_dir("./wwwroot");svr.Post("/run", [](const Request & req, Response &res) {std::string in_json = req.body;std::string out_json;CompileAndRun::Start(in_json, &out_json);res.set_content(out_json, "application/json");});// 启动服务器svr.listen("0.0.0.0", 8080);return 0;
}

总结

这个简易在线编译器只是我用于学习多进程应用与前后端交互而写的超微小项目,不算上前端网页只有500行代码左右。虽然还有很多设计不完善的地方,但凭我现在的水平也暂时想不到更好的解决方案,有什么不足的地方,欢迎讨论。

github:完整代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3225005.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

深入了解线程锁的使用及锁的本质

文章目录 线程锁的本质局部锁的使用 锁的封装及演示线程饥饿问题 线程加锁本质可重入和线程安全死锁问题 根据前面内容的概述, 上述我们已经知道了在linux下关于线程封装和线程互斥,锁的相关的概念, 下面就来介绍一下关于线程锁的一些其他概念. 线程锁的本质 当这个锁是全局的…

Vue使用Echarts(入门级)

最终效果&#xff1a; npm install echarts --save // 先安装echarts<template><!-- 创建一个dom区域用于挂载echarts图表 --><div id"chart" style"width: 600px;height:500px;"/> </template> <script> import * as ech…

WPF依赖附加属性

依赖附加属性的定义 基本过程&#xff1a;声明、注册、包装 依赖附加属性必须在依赖对象&#xff0c;附加属性不一定&#xff0c;关注的是被附加的对象是否是依赖对象 快捷方式&#xff1a;propa tab 关键字&#xff1a;RegisterAttached // 方法封装 public static int …

4.MkDocs样式

学习 Admonitions(警告) - Material for MkDocs (wdk-docs.github.io) 提示 - Material for MkDocs 中文文档 (llango.com) Buttons(按钮) - Material for MkDocs (wdk-docs.github.io) 建议去看这些网站&#xff0c;更为详细。 常用功能 便利贴 ​​ 开启 markdown_ex…

FL Studio 24.1.1.4234 (Windows) / 24.1.1.3884 (Mac OS X)

FL Studio 24.1.1.4234 (Windows) / 24.1.1.3884 (Mac OS X) 主页多媒体音频编辑FL Studio 24.1.1.4234 (Windows) / 24.1.1.3884... FL Studio 图标 FL Studio&#xff08;前身为 FruityLoops&#xff09;是一款功能强大的音乐制作环境或数字音频工作站&#xff08;DAW&#x…

基于Java的科大讯飞大模型API调用实现

写在前面&#xff1a;因为现在自己实习的公司新拓展的一个业务是结合AI的低代码平台&#xff0c;我负责后端的开发&#xff0c;之前一直都是直接使用gpt或者文心一言等ui界面来直接使用大模型&#xff0c;从来没有自己调接口过&#xff0c;所以本文记录一下自己第一次使用大模型…

【密码学】分组密码概述

一、分组密码的定义 分组密码和流密码都是对称密码体制。 流密码&#xff1a;是将明文视为连续的比特流&#xff0c;对每个比特或字节进行实时加密&#xff0c;而不将其分割成固定的块。流密码适用于加密实时数据流&#xff0c;如网络通信。分组密码&#xff1a;是将明文数据…

【无聊找点事干】局域网内把window 搭建为代理服务器上网

场景描述 同一局域网内&#xff0c;server 2012可以上网&#xff0c;window 10无法上网。现在将电脑server 2012设置为代理服务器&#xff0c;使得window 10可以通过server 2012访问公网。 server 2012&#xff1a;服务端 安装代理服务器软件&#xff1a;CCProxy点击‘设置’…

『大模型笔记』GraphRAG:利用复杂信息进行发现的新方法!

GraphRAG:利用复杂信息进行发现的新方法! 文章目录 一. GraphRAG:利用复杂信息进行发现的新方法!1. 将RAG应用于私人数据集2. 整个数据集的推理3. 创建LLM生成的知识图谱4. 结果指标5. 下一步二. 参考文献微软官方推文:https://www.microsoft.com/en-us/research/blog/gra…

综合安全防护

题目 1,DMZ区内的服务器,办公区仅能在办公时间内(9:00-18:00)可以访问,生产区的设备全天可以访问. 2,生产区不允许访问互联网,办公区和游客区允许访问互联网 3,办公区设备10.0.2.10不允许访问DMz区的FTP服务器和HTTP服务器,仅能ping通10.0.3.10 4,办公区分为市场部和研发部,研…

activemq-CVE-2022-41678

Apache ActiveMQ Jolokia 后台远程代码执行漏洞 Apache ActiveMQ在5.16.5&#xff0c;5.17.3版本及以前&#xff0c;后台Jolokia存在一处任意文件写入导致的远程代码执行漏洞。 启动环境 admin/admin 方法一&#xff1a;利用poc 这个方法受到ActiveMQ版本的限制&#xff0c;因…

化妆品3D虚拟三维数字化营销展示更加生动、真实、高效!

随着人们越来越追求高速便捷的生活工作方式&#xff0c;企业在营销市场也偏国际化&#xff0c;借助VR全景制作技术&#xff0c;将企业1:1复刻到云端数字化世界&#xff0c;能带来高沉浸式的逼真、震撼效果。 通过我们独特的漫游点自然场景过渡技术&#xff0c;您将置身于一个真…

Java | Leetcode Java题解之第225题用队列实现栈

题目&#xff1a; 题解&#xff1a; class MyStack {Queue<Integer> queue;/** Initialize your data structure here. */public MyStack() {queue new LinkedList<Integer>();}/** Push element x onto stack. */public void push(int x) {int n queue.size();…

Qt Creator仿Visual Studio黑色主题

转自本人博客&#xff1a;Qt Creator仿Visual Studio黑色主题 1.演示 配置文件和步骤在后面&#xff0c;先看成品&#xff0c;分别是QWidget和QML的代码编写界面&#xff1a; 2. 主题配置文件 下载链接&#xff1a;QtCreator _theme_VS_dark.xml 也可以自己新建一个xml文件&…

理解局域网技术:从基础到进阶

局域网&#xff08;LAN&#xff09;是在20世纪70年代末发展起来的&#xff0c;起初主要用于连接单位内部的计算机&#xff0c;使它们能够方便地共享各种硬件、软件和数据资源。局域网的主要特点是网络为一个单位所拥有&#xff0c;地理范围和站点数目均有限。 局域网技术在计算…

后端——全局异常处理

一、老办法try-catch 当我们执行一些错误操作导致程序报错时&#xff0c;程序会捕捉到异常报错&#xff0c;这个异常会存在一个Exception对象里 那我们在spring boot工程开发时&#xff0c;当我们执行一个sql查询时报错了&#xff0c;那就会从最底层的Mapper层捕捉到Exceptio…

《RWKV》论文笔记

原文出处 [2305.13048] RWKV: Reinventing RNNs for the Transformer Era (arxiv.org) 原文笔记 What RWKV(RawKuv):Reinventing RNNs for the Transformer Era 本文贡献如下&#xff1a; 提出了 RWKV 网络架构&#xff0c;结合了RNNS 和Transformer 的优点&#xff0c;同…

Golang | Leetcode Golang题解之第225题用队列实现栈

题目&#xff1a; 题解&#xff1a; type MyStack struct {queue []int }/** Initialize your data structure here. */ func Constructor() (s MyStack) {return }/** Push element x onto stack. */ func (s *MyStack) Push(x int) {n : len(s.queue)s.queue append(s.queu…

基于信号量的生产者消费者模型

文章目录 信号量认识概念基于线程分析信号量信号量操作 循环队列下的生产者消费者模型理论认识代码部分 信号量 认识概念 信号量本质: 计数器 它也叫做公共资源 为了线程之间,进程间通信------>多个执行流看到的同一份资源---->多个资源都会并发访问这个资源(此时易出现…