【C++20工程实战】自己动手实现纯头文件日志库

文章目录

  • 一、std::format
  • 二、std::source_location
  • 三、detail名字空间
  • 四、X-macro技术
  • 五、cpp20的log
  • 参考

一、std::format

GCC 13, CLANG 14 and MSVC 16.10/VS 2019 all have the {fmt} based std::format available in respective standard libraries.

基本字符串格式化:

#include <format>
#include <iostream>int main() {std::string message = std::format("Hello, {}!", "World");std::cout << message << std::endl;  // 输出: Hello, World!return 0;
}

多个参数:

#include <format>
#include <iostream>int main() {std::string message = std::format("{}, you have {} new messages", "Alice", 5);std::cout << message << std::endl;  // 输出: Alice, you have 5 new messagesreturn 0;
}

指定宽度和填充:

#include <format>
#include <iostream>int main() {std::string message = std::format("{:*>10}", 42);std::cout << message << std::endl;  // 输出: *******42return 0;
}

格式说明符

  • std::format 支持多种格式说明符,类似于 printf,但更灵活和强大。

整数:

#include <format>
#include <iostream>int main() {std::string dec = std::format("{:d}", 42);  // 十进制std::string hex = std::format("{:x}", 42);  // 十六进制std::string oct = std::format("{:o}", 42);  // 八进制std::cout << "Decimal: " << dec << std::endl;  // 输出: Decimal: 42std::cout << "Hexadecimal: " << hex << std::endl;  // 输出: Hexadecimal: 2astd::cout << "Octal: " << oct << std::endl;  // 输出: Octal: 52return 0;
}

浮点数:

#include <format>
#include <iostream>int main() {std::string fixed = std::format("{:.2f}", 3.14159);  // 定点表示,保留两位小数std::string sci = std::format("{:.2e}", 3.14159);    // 科学计数法std::cout << "Fixed: " << fixed << std::endl;  // 输出: Fixed: 3.14std::cout << "Scientific: " << sci << std::endl;  // 输出: Scientific: 3.14e+00return 0;
}

对齐和填充:

#include <format>
#include <iostream>int main() {std::string left = std::format("{:<10}", "left");   // 左对齐std::string right = std::format("{:>10}", "right"); // 右对齐std::string center = std::format("{:^10}", "center"); // 居中对齐std::cout << "Left: " << left << std::endl;  // 输出: Left: left      std::cout << "Right: " << right << std::endl;  // 输出: Right:     rightstd::cout << "Center: " << center << std::endl;  // 输出: Center:   center  return 0;
}

指定填充字符:

#include <format>
#include <iostream>int main() {std::string padded = std::format("{:*>10}", 42);  // 用 '*' 填充,右对齐std::cout << padded << std::endl;  // 输出: *******42return 0;
}

套格式化:

  • 你可以将格式化字符串作为参数传递,进行嵌套格式化:
#include <format>
#include <iostream>int main() {std::string nested = std::format("Result: {}", std::format("{:.2f}", 3.14159));std::cout << nested << std::endl;  // 输出: Result: 3.14return 0;
}

自定义类型格式化:

  • 你可以通过定义 formatter 特化来格式化自定义类型:
  • 为 Point 结构体特化 std::formatter 模板。特化的模板需要实现两个函数:parse 和 format。
#include <format>
#include <iostream>// 定义 Point 结构体
struct Point {int x, y;
};// 为 Point 特化 std::formatter 模板
template <>
struct std::formatter<Point> {// 支持的格式说明符constexpr auto parse(format_parse_context& ctx) {// 这里你可以解析特定的格式说明符,如果有的话auto it = ctx.begin();auto end = ctx.end();if (it != end && (*it == 'f' || *it == 'd')) {++it;}// 检查格式字符串是否正确if (it != end && *it != '}') {throw format_error("invalid format");}// 返回格式说明符的结束位置return it;}// 格式化函数auto format(const Point& p, format_context& ctx) const {// 格式化输出return format_to(ctx.out(), "({}, {})", p.x, p.y);}
};int main() {Point p{1, 2};std::string pointStr = std::format("Point: {}", p);std::cout << pointStr << std::endl;  // 输出: Point: (1, 2)return 0;
}

二、std::source_location

std::source_location 提供了一种获取编译时源代码位置信息的便捷方法;

std::source_location 是 C++20 引入的一个标准库特性,用于获取代码的编译时信息,如文件名、行号、列号和函数名。这对于调试和日志记录非常有用,因为它可以在运行时捕获这些信息,而不需要手动提供。

基本用法

  • std::source_location 类似于传统的预处理器宏(如 __FILE__ 和 __LINE__),但提供了更灵活和安全的接口。
#include <iostream>
#include <source_location>void logMessage(const std::string& message, const std::source_location& location = std::source_location::current()) {std::cout << "Message: " << message << "\n"<< "File: " << location.file_name() << "\n"<< "Line: " << location.line() << "\n"<< "Column: " << location.column() << "\n"<< "Function: " << location.function_name() << "\n";
}int main() {logMessage("This is a log message");return 0;
}

std::source_location 类:

  • std::source_location 是一个不可变(immutable)的类,它包含了与源代码位置相关的信息。
  • 常用的成员函数有:
    file_name(): 返回当前文件名的字符串。
    line(): 返回当前行号。
    column(): 返回当前列号。
    function_name(): 返回当前函数名。

在 logMessage 函数中,location 参数的默认值是 std::source_location::current(),它捕获调用 logMessage 时的源代码位置。 这使得调用者无需显式提供源代码位置,编译器会自动提供。

自定义日志宏

  • 你可以定义一个宏来简化日志记录,利用 std::source_location 捕获源代码位置。
#include <iostream>
#include <source_location>#define LOG_MESSAGE(msg) logMessage(msg)void logMessage(const std::string& message, const std::source_location& location = std::source_location::current()) {std::cout << "Message: " << message << "\n"<< "File: " << location.file_name() << "\n"<< "Line: " << location.line() << "\n"<< "Column: " << location.column() << "\n"<< "Function: " << location.function_name() << "\n";
}int main() {LOG_MESSAGE("This is a log message");return 0;
}

捕获异常位置

  • 可以在异常处理时使用 std::source_location 捕获抛出异常的位置,从而提供更详细的错误信息。
#include <iostream>
#include <stdexcept>
#include <source_location>void throwError(const std::string& message, const std::source_location& location = std::source_location::current()) {throw std::runtime_error(std::format("Error: {}\nFile: {}\nLine: {}\nColumn: {}\nFunction: {}",message, location.file_name(), location.line(), location.column(), location.function_name()));
}int main() {try {throwError("An example error");} catch (const std::runtime_error& e) {std::cerr << e.what() << std::endl;}return 0;
}

三、detail名字空间

details名字空间实现内部函数的封装,对外隐藏细节,只暴露必要API函数

#include <condition_variable>
#include <functional>
#include <future>
#include <iostream>
#include <mutex>
#include <queue>
#include <stdexcept>
#include <thread>
#include <utility>
#include <vector>class ThreadPool {public:explicit ThreadPool(size_t numThreads);~ThreadPool();template <class F, class... Args>auto enqueue(F&& f, Args&&... args)-> std::future<typename std::result_of<F(Args...)>::type>;private:// Worker threadsstd::vector<std::thread> workers;// Task queuestd::queue<std::function<void()>> tasks;// Synchronizationstd::mutex queueMutex;std::condition_variable condition;bool stop;// Internal implementation detailsvoid worker();
};// Implementation of ThreadPool methodsThreadPool::ThreadPool(size_t numThreads) : stop(false) {for (size_t i = 0; i < numThreads; ++i) {workers.emplace_back(&ThreadPool::worker, this);}
}ThreadPool::~ThreadPool() {{std::unique_lock<std::mutex> lock(queueMutex);stop = true;}condition.notify_all();for (std::thread& worker : workers) {worker.join();}
}template <class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)-> std::future<typename std::result_of<F(Args...)>::type> {using return_type = typename std::result_of<F(Args...)>::type;//创建一个 std::packaged_task 对象并包装一个可调用对象(任务)//它会将任务的结果保存到一个与之关联的 std::promise 对象中。auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));std::future<return_type> res = task->get_future();{std::unique_lock<std::mutex> lock(queueMutex);if (stop) {throw std::runtime_error("enqueue on stopped ThreadPool");}tasks.emplace([task]() { (*task)(); });}condition.notify_one();return res;
}void ThreadPool::worker() {while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(queueMutex);condition.wait(lock, [this] { return stop || !tasks.empty(); });if (stop && tasks.empty()) {return;}task = std::move(tasks.front());tasks.pop();}task();}
}int main() {ThreadPool pool(4);auto result = pool.enqueue([](int answer) { return answer; }, 42);std::cout << "The answer is " << result.get() << std::endl;return 0;
}

测试:

Program returned: 0
Program stdout
The answer is 42

std::packaged_task 和 std::future 的结合使用的例子:

#include <iostream>
#include <future>
#include <thread>
#include <chrono>// 一个简单的任务函数,返回输入值的两倍
int doubleValue(int x) {std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟长时间任务return x * 2;
}int main() {// 创建一个 std::packaged_task 对象并包装 doubleValue 函数std::packaged_task<int(int)> task(doubleValue);// 获取与该任务关联的 std::future 对象std::future<int> result = task.get_future();// 启动一个线程来异步执行该任务std::thread t(std::move(task), 10);// 在主线程中可以做其他工作// 等待任务完成并获取结果std::cout << "Result: " << result.get() << std::endl;// 等待线程完成t.join();return 0;
}

std::promise 和 std::future 结合使用的例子:

#include <iostream>
#include <future>
#include <thread>
#include <chrono>// 一个简单的任务函数,返回输入值的两倍
int doubleValue(int x) {std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟长时间任务return x * 2;
}int main() {// 创建一个 std::promise 对象std::promise<int> promise;// 获取与该 promise 关联的 std::future 对象std::future<int> result = promise.get_future();// 启动一个线程来异步执行任务,并使用 lambda 设置 promise 的值std::thread t([&promise](int x) {// 执行任务并设置 promise 的值promise.set_value(doubleValue(x));}, 10);// 在主线程中可以做其他工作// 等待任务完成并获取结果std::cout << "Result: " << result.get() << std::endl;// 等待线程完成t.join();return 0;
}

四、X-macro技术

X-macro技术的基本思想是使用预处理器宏来定义一组数据或操作,然后通过另一个宏来实际展开这些数据或操作。

eg:假设我们有一组颜色定义,并希望使用这些定义来生成枚举和字符串数组。传统的做法需要分别定义这些内容,可能导致重复代码。
X-macro技术可以帮助我们解决这个问题。

#include <stdio.h>//首先,定义一个包含所有颜色的宏列表:
#define COLOR_LIST \X(RED)         \X(GREEN)       \X(BLUE)        \X(YELLOW)//接下来,使用这个宏列表生成枚举
//这里,我们定义了一个宏 X,它会在 COLOR_LIST 中被展开,每个颜色都会生成相应的枚举值。
typedef enum {#define X(color) color,COLOR_LIST#undef X
} Color;//然后,可以使用相同的宏列表生成字符串数组:
const char* ColorNames[] = {#define X(color) #color,COLOR_LIST#undef X
};int main() {for (int i = 0; i < sizeof(ColorNames)/sizeof(ColorNames[0]); ++i) {printf("Color %d: %s\n", i, ColorNames[i]);}return 0;
}

测试:

Program returned: 0
Program stdout
Color 0: RED
Color 1: GREEN
Color 2: BLUE
Color 3: YELLOW

五、cpp20的log

旧的打印方法:

#include <format>
#include <source_location>
#include <iostream>void log(std::string msg, const char* file, int line)
{std::cout<< file << ":"<<line<< " [Info] "<<msg<<'\n';
}#define LOG(msg) log(msg,__FILE__,__LINE__)int main()
{LOG("wangji");return 0;
}

使用source_location,source_location写在默认参数的位置(内部实现buildlocation),实际是打印时是调用者的信息

#include <format>
#include <source_location>
#include <iostream>void log(std::string msg, std::source_location loc=std::source_location::current())
{std::cout<< loc.file_name() << ":"<<loc.line()<< " [Info] "<<msg<<'\n';
}#define LOG(msg) log(msg)int main()
{LOG("wangji");return 0;
}

结合std::format,把format的参数抄过来
在这里插入图片描述

#include <format>
#include <iostream>
#include <source_location>template <class T>
struct with_source_location {private:T inner;std::source_location loc;public:template <class U>requires std::constructible_from<T, U>consteval with_source_location(U &&inner, std::source_location loc = std::source_location::current()): inner(std::forward<U>(inner)), loc(std::move(loc)) {}constexpr T const &format() const { return inner; }constexpr std::source_location const &location() const { return loc; }
};template <typename... Args>
void log_info(with_source_location<std::format_string<Args...>> fmt,Args &&...args) {auto const &loc = fmt.location();std::cout << loc.file_name() << ":" << loc.line() << " [Info] "<< std::vformat(fmt.format().get(),std::make_format_args(args...))<< '\n';
}int main() {log_info("wangji1999");return 0;
}

测试:

Program returned: 0
Program stdout
/app/example.cpp:35 [Info] wangji1999

使用X macro技术来定义日志等级

很神奇的是,最后宏展开后,最后一个枚举后面没有增加逗号
在这里插入图片描述

#include <format>
#include <iostream>
#include <source_location>
#include <cstdint>#define MINILOG_FOREACH_LOG_LEVEL(f) \f(trace) \f(debug) \f(info) \f(critical) \f(warn) \f(error) \f(fatal)enum class log_level : std::uint8_t {
#define _FUNCTION(name) name,MINILOG_FOREACH_LOG_LEVEL(_FUNCTION)
#undef _FUNCTION
};//cpp17支持inline修饰全局唯一的全局变量
inline std::string log_level_name(log_level lev) {switch (lev) {
#define _FUNCTION(name) case log_level::name: return #name;MINILOG_FOREACH_LOG_LEVEL(_FUNCTION)
#undef _FUNCTION}return "unknown";
}template <class T>
struct with_source_location {private:T inner;std::source_location loc;public://consteval只能编译器调用,constexpr既可以是编译期也可以是运行期调用template <class U>requires std::constructible_from<T, U>consteval with_source_location(U &&inner, std::source_location loc = std::source_location::current()): inner(std::forward<U>(inner)), loc(std::move(loc)) {}constexpr T const &format() const { return inner; }constexpr std::source_location const &location() const { return loc; }
};//cpp17支持inline修饰全局唯一的全局变量
inline log_level max_level=log_level::info;template <typename... Args>
void generic_log(log_level lev, with_source_location<std::format_string<Args...>> fmt,Args &&...args) 
{if (lev>= max_level){auto const &loc = fmt.location();std::cout << loc.file_name() << ":" << loc.line() << " [Info] "<< std::vformat(fmt.format().get(),std::make_format_args(args...))<< '\n';}
}//X macro技术封装不同等级的日志函数
#define _FUNCTION(name) \
template <typename... Args> \
void log_##name(with_source_location<std::format_string<Args...>> fmt, Args &&...args) { \return generic_log(log_level::name, std::move(fmt), std::forward<Args>(args)...); \
}
MINILOG_FOREACH_LOG_LEVEL(_FUNCTION)
#undef _FUNCTIONint main() {generic_log(log_level::debug ,"wangji {}", "hi");generic_log(log_level::info ,"wangji {}", "hi");log_debug( "wangji {}", "hi");log_info("wangji {}", "hi");return 0;
}

注意:
全局的模板函数和直接在类内定义的成员函数,cpp自动给你加上inline

为了防止多个log模块冲突,需要加上namespace,注意宏是怎么加的,要在内部加;

不想暴露给用户的函数,用details的namespce包起来,但是用户仍然可以拿到

#include <format>
#include <iostream>
#include <source_location>
#include <cstdint>
#include <string>
#include <fstream>
#include <chrono>namespace minilog{#define MINILOG_FOREACH_LOG_LEVEL(f) \f(trace) \f(debug) \f(info) \f(critical) \f(warn) \f(error) \f(fatal)enum class log_level : std::uint8_t {
#define _FUNCTION(name) name,MINILOG_FOREACH_LOG_LEVEL(_FUNCTION)
#undef _FUNCTION
};//不想暴露给用户的函数,用details的namespce包起来,但是用户仍然可以拿到
namespace detail{//给日志设置颜色,ansi控制码,1m表示强调色
//\E,\033是一样的
#if defined(__linux__) || defined(__APPLE__)
inline constexpr char k_level_ansi_colors[(std::uint8_t)log_level::fatal + 1][8] = {"\E[37m","\E[35m","\E[32m","\E[34m","\E[33m","\E[31m","\E[31;1m",
};
inline constexpr char k_reset_ansi_color[4] = "\E[m";
#define _MINILOG_IF_HAS_ANSI_COLORS(x) x
#else
#define _MINILOG_IF_HAS_ANSI_COLORS(x)
inline constexpr char k_level_ansi_colors[(std::uint8_t)log_level::fatal + 1][1] = {"","","","","","","",
};
inline constexpr char k_reset_ansi_color[1] = "";
#endifinline std::string log_level_name(log_level lev) {switch (lev) {
#define _FUNCTION(name) case log_level::name: return #name;MINILOG_FOREACH_LOG_LEVEL(_FUNCTION)
#undef _FUNCTION}return "unknown";
}inline log_level log_level_from_name(std::string lev){
#define _FUNCTION(name) if (lev == #name) return log_level::name;MINILOG_FOREACH_LOG_LEVEL(_FUNCTION)
#undef _FUNCTIONreturn log_level::info;
}template <class T>
struct with_source_location {private:T inner;std::source_location loc;public:template <class U>requires std::constructible_from<T, U>consteval with_source_location(U &&inner, std::source_location loc = std::source_location::current()): inner(std::forward<U>(inner)), loc(std::move(loc)) {}constexpr T const &format() const { return inner; }constexpr std::source_location const &location() const { return loc; }
};//通过环境变量设置loglevel
inline log_level g_max_level=[]()->log_level{auto lev=std::getenv("MINILOG_LEVEL");if (lev){return log_level_from_name(lev);}else{return log_level::info;}
}();//自定义输出文件
inline std::ofstream g_log_file=[]()->std::ofstream{auto path=std::getenv("MINILOG_FILE");if (path){return std::ofstream(path, std::ios::app);}else{return std::ofstream();}
}();inline void output_log(log_level lev, std::string msg, std::source_location const &loc) {//增加时间戳,cpp20std::chrono::zoned_time now{std::chrono::current_zone(), std::chrono::high_resolution_clock::now()};msg = std::format("{} {}:{} [{}] {}", now, loc.file_name(), loc.line(), log_level_name(lev), msg);if (g_log_file) {g_log_file << msg + '\n';}if (lev >= detail::g_max_level) {std::cout << _MINILOG_IF_HAS_ANSI_COLORS(k_level_ansi_colors[(std::uint8_t)lev] +)msg _MINILOG_IF_HAS_ANSI_COLORS(+ k_reset_ansi_color) + '\n';}
}}//追加写
inline void set_log_file(std::string path) {detail::g_log_file = std::ofstream(path, std::ios::app);
}//一般不直接暴露变量给外面,而是用过某个函数设置日志等级
inline void set_log_level(log_level lev){detail::g_max_level = lev;
}template <typename... Args>
void generic_log(log_level lev, detail::with_source_location<std::format_string<Args...>> fmt,Args &&...args) 
{if (lev>= detail::g_max_level){auto const &loc = fmt.location();//cout的线程安全问题,多次使用cout可能不是线程安全的,一次使用则是线程安全的std::cout _MINILOG_IF_HAS_ANSI_COLORS(<< detail::k_level_ansi_colors[(std::uint8_t)lev])<< loc.file_name() << ":" << loc.line() <<" [" <<detail::log_level_name(lev)<< "] "<< std::vformat(fmt.format().get(), std::make_format_args(args...))_MINILOG_IF_HAS_ANSI_COLORS(<< detail::k_reset_ansi_color)<< '\n';}
}#define _FUNCTION(name) \
template <typename... Args> \
void log_##name(detail::with_source_location<std::format_string<Args...>> fmt, Args &&...args) { \return generic_log(log_level::name, std::move(fmt), std::forward<Args>(args)...); \
}
MINILOG_FOREACH_LOG_LEVEL(_FUNCTION)
#undef _FUNCTION//直接用这个宏,这个宏不遵守namspace,定义宏的时候,日志宏名称的前缀使用namespace作为前缀
#define MINILOG_P(x) ::minilog::log_info(#x "={}", x)}int main() {MINILOG_P("100");::minilog::log_fatal("123");::minilog::log_debug("123");minilog::set_log_level(minilog::log_level::trace); // default log level is info::minilog::log_debug("123");std::cout<<"============"<<std::endl;#define _FUNCTION(name) minilog::log_##name(#name);MINILOG_FOREACH_LOG_LEVEL(_FUNCTION)#undef _FUNCTIONreturn 0;
}

测试:
在这里插入图片描述

参考

  • 【C++20工程实战】自己动手实现纯头文件日志库
  • 项目源码

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《精通ChatGPT:从入门到大师的Prompt指南》附录A:常用Prompt示例

附录A&#xff1a;常用Prompt示例 在《精通ChatGPT&#xff1a;从入门到大师的Prompt指南》的附录A中&#xff0c;我们将展示一系列常用的Prompt示例&#xff0c;帮助读者更好地理解和应用Prompt技术。每个示例将包含Prompt的描述、使用场景、预期结果以及实际输出。希望这些示…

LLVM Cpu0 新后端10

想好好熟悉一下llvm开发一个新后端都要干什么&#xff0c;于是参考了老师的系列文章&#xff1a; LLVM 后端实践笔记 代码在这里&#xff08;还没来得及准备&#xff0c;先用网盘暂存一下&#xff09;&#xff1a; 链接: https://pan.baidu.com/s/1yLAtXs9XwtyEzYSlDCSlqw?…

Centos修改默认端口22

修改Centos服务器ssh链接的默认端口22到任意端口&#xff0c;主要两个步骤&#xff1a; 修改配置文件&#xff0c;添加端口开放防火墙 一、 vim /etc/ssh/sshd_config 在文件中 #Port 22 行下增加 # Port 22 Port [修改后端口]注意&#xff1a; 这里 先将其中的#Port 22前的…

ffmpeg视频解码原理和实战-(5)硬件加速解码后进行渲染并输出帧率

头文件&#xff1a; xvideoview.h #ifndef XVIDEO_VIEW_H #define XVIDEO_VIEW_H #include <mutex> #include <fstream> struct AVFrame;void MSleep(unsigned int ms);//获取当前时间戳 毫秒 long long NowMs();/// 视频渲染接口类 /// 隐藏SDL实现 /// 渲染方案…

【电机控制】FOC算法验证步骤

【电机控制】FOC算法验证步骤 文章目录 前言一、PWM——不接电机1、PWMA-H-50%2、PWMB-H-25%3、PWMC-H-0%4、PWMA-L-50%5、PWMB-L-75%6、PWMC-L-100% 二、ADC——不接电机1.电流零点稳定性、ADC读取的OFFSET2.电流钳准备3.运放电路分析1.电路OFFSET2.AOP3.采样电路的采样值范围…

归并排序——逆序数对的统计

逆序数对的统计 题目描述 运行代码 #include <iostream> using namespace std; #define LL long long const int N 1e5 5; int a[N], tmp[N]; LL merge_sort(int q[], int l, int r) {if (l > r)return 0; int mid l r >> 1; LL res merge_sort(q, l,…

【数据结构(邓俊辉)学习笔记】图07——最短路径

文章目录 0. 概述1. 问题2. 最短路径2.1 最短路径树2.1.1 单调性2.1.2 歧义性2.1. 3 无环性 2.2 Dijkstra 算法2.2.1 贪心迭代2.2.2 实现2.2.3 实例2.2.4 复杂度 0. 概述 学习下最短路径和Dijistra算法 1. 问题 给定带权网络G (V, E)&#xff0c;以及源点&#xff08;source…

4.大模型微调技术LoRA

大模型低秩适配(LoRA)技术 现有PEFT 方法的局限与挑战 Adapter方法,通过增加模型深度而额外增加了模型推理延时。Prompt Tuning、Prefix Tuning、P-Tuning等方法中的提示较难训练,同时缩短了模型可用的序列长度。往往难以同时实现高效率和高质量,效果通常不及完全微调(f…

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《计及电力不平衡风险的配电网分区协同规划》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

探究IOC容器刷新环节初始化前的预处理

目录 一、IOC容器的刷新环节快速回顾 二、初始化前的预处理prepareRefresh源码分析 三、初始化属性源 &#xff08;一&#xff09;GenericWebApplicationContext初始化属性源 &#xff08;二&#xff09;StaticWebApplicationContext初始化属性源 四、初始化早期事件集合…

3.大模型高效微调PEFT

大模型高效微调(PEFT)技术 预训练模型的背景 预训练与微调:传统的微调方法通常涉及对整个预训练模型的参数进行再训练,以适应特定任务。这虽然有效,但计算成本高,且需要大量的标记数据。模型结构:像BERT或GPT这样的模型通常包含数亿甚至数十亿个参数,构成一个深层次的…

Qt——升级系列(Level Four):控件概述、QWidget 核心属性、按钮类控件

目录 控件概述 QWidget 核心属性 核心属性概览 enabled geometry windowTitle windowIcon windowOpacity cursor font toolTip focusPolicy styleSheet 按钮类控件 Push Button Radio Buttion Check Box Tool Button 控件概述 Widget 是 Qt 中的核⼼概念. 英⽂原义是 "…

西门子学习笔记11 - PTO脉冲指令的使用

1、使用指令前的设置 1、打开一个脉冲发生器&#xff0c;并启用 2、选择使用PTO(脉冲A和方向B) 3、硬件设置输出 4、这样前期的准备工作就完成了 2、指令的使用 1、添加指令CTRL_PTO 2、配置如下 3、方向控制程序如下 4、最后进行测试即可

C语言之存储类、作用域、生命周期、链接属性

一 &#xff1a;概念解析 1&#xff1a; 存储类 &#xff08;1&#xff09;存储类就是存储类型&#xff0c;就是描述C语言变量存储在什么地方 &#xff08;2&#xff09;内存有多种管理方法&#xff1a;栈、堆数据段、bss段、.text段......一个变量的存储类属性就是描述…

html--万年历

<!DOCTYPE html> <html lang"zh_CN"><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8" /><meta charset"utf-8" /><title>万年历</title><link rel"styles…

C语言 | Leetcode C语言题解之第142题环形链表II

题目&#xff1a; 题解&#xff1a; struct ListNode* detectCycle(struct ListNode* head) {struct ListNode *slow head, *fast head;while (fast ! NULL) {slow slow->next;if (fast->next NULL) {return NULL;}fast fast->next->next;if (fast slow) {s…

LLVM Cpu0 新后端9 objdump readelf

想好好熟悉一下llvm开发一个新后端都要干什么&#xff0c;于是参考了老师的系列文章&#xff1a; LLVM 后端实践笔记 代码在这里&#xff08;还没来得及准备&#xff0c;先用网盘暂存一下&#xff09;&#xff1a; 链接: https://pan.baidu.com/s/1yLAtXs9XwtyEzYSlDCSlqw?…

EE trade:如何在A股市场中有效设定止盈止损点

A股市场充满机遇和风险&#xff0c;很多投资者在这里实现了财富增长&#xff0c;也有投资者在这里遭受损失。如何在波动性较大的市场中&#xff0c;控制风险&#xff0c;保护利润和本金?止盈止损是关键。 什么是止盈止损? 止盈止损是指在交易中&#xff0c;根据预先设定的条…

如何稳定高效地进行 TiDB 数据导入导出?

对于在数据库行业中摸爬滚打多年的老鸟 DBA 来说&#xff0c;TiDB 可是一点也不陌生&#xff0c;作为 PingCAP 公司自主研发的真开源分布式数据库&#xff0c;其先进的设计理念以及丰富的生态工具&#xff0c;可算得上是业界自主创新和性能领先的代名词。 TiDB 是谁&#xff1…

MAVEN架构项目管理工具

1、什么是maven Maven是跨平台的项目管理工具。主要服务于基于Java平台的项目构建&#xff0c;依赖管理和项目信息管理。 2、maven的目标&#xff1a;Maven的主要目标是为了使开发人员在最短的时间内领会项目的所有状态 3、使用maven不需要考虑各个依赖的版本&#xff0c;因…