【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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值