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

在C++上实现反射用法

0. 简介

最近看很多端到端的工作,发现大多数都是基于mmdet3d来做的,而这个里面用的比较多的形式就是反射机制,这样其实可以比较好的通过类似plugin的形式完成模型模块的插入。当然我们这里不是来分析python的反射机制的。我们这篇文章主要来介绍C++上实现反射。

1. 反射的用途

一般来说就是序列化反序列化啦。比如说你想通过网络传递一个实例,或者把它保存到文件里,以后再取出来放到程序里,这就需要反射

反射其实还能细分为静态反射和动态反射

  1. 静态反射,就是在编译期生成反射信息
  2. 动态反射,就是在运行时生成反射信息
  3. 动态反射,显然需要一套强大的运行时和动态类型系统,也是显然的很复杂

在来,还有侵入式非侵入式之分。非侵入式的反射允许不对源码进行修改就能实现反射;侵入式呢就得对源码动动手脚了

第一种:实现思路,是在源码里加入大量的反射信息,手动注册反射。 这种库的代表是rttr

第二种:实现思路是通过parser解析源码,自动生成反射信息。
这种库的代表是QT,UE的反射系统

第三种: 用大量的编译期模板生成元信息,然后构建一套巨抽象的运行时,比如Ubpa/UDRefl

第四种: 利用调试器的运行时信息来生成反射代码,这种想法并非无稽之谈,思考下,lldb,gdb明显能在运行时获取字段,内容,类型

第五种: 绑架编译器! clang提供了插件功能。事实上也有大佬做了,这些都有比较详细的例子。

2. 源码添加,手动注册

这种用的是比较多的,一般的是自定义一个反射类,然后用模板来实现一个模板类管理类名和类构造函数的映射关系,并提供构造对象的接口,每个基类需要初始化一个这样的管理对象。

下面我们提供一个对应的 static 模板函数,用来保存和返回对应的管理对象。并使用模板函数和 new 操作符作为每个类的构造函数。
实现一个简单的 helper 模板类提供作为注册的简单封装,并封装宏实现注册。下面是具体代码:

#ifndef __BASE_H__
#define __BASE_H__
#include <string>
#include <map>
#include <iostream>// 使用模板,每个基类单独生成一个 ClassRegister
// 好处是需要反射的类不需要去继承 Object 对象
// ClassRegister 用来管理类名->类构造函数的映射,对外提供根据类名构造对象对函数
template<typename ClassName>
class ClassRegister {public:typedef ClassName* (*Constructor)(void);private:typedef std::map<std::string, Constructor> ClassMap;ClassMap constructor_map_;public:// 添加新类的构造函数void AddConstructor(const std::string class_name, Constructor constructor) {typename ClassMap::iterator it = constructor_map_.find(class_name);if (it != constructor_map_.end()) {std::cout << "error!";return;}constructor_map_[class_name] = constructor;}// 根据类名构造对象ClassName* CreateObject(const std::string class_name) const {typename ClassMap::const_iterator it = constructor_map_.find(class_name);if (it == constructor_map_.end()) {return nullptr;}return (*(it->second))();}
};// 用来保存每个基类的 ClassRegister static 对象,用于全局调用
template <typename ClassName>
ClassRegister<ClassName>& GetRegister() {static ClassRegister<ClassName> class_register;return class_register;
}// 每个类的构造函数,返回对应的base指针
template <typename BaseClassName, typename SubClassName>
BaseClassName* NewObject() {return new SubClassName();
}// 为每个类反射提供一个 helper,构造时就完成反射函数对注册
template<typename BaseClassName>
class ClassRegisterHelper {public:ClassRegisterHelper(const std::string sub_class_name,typename ClassRegister<BaseClassName>::Constructor constructor) {GetRegister<BaseClassName>().AddConstructor(sub_class_name, constructor);}~ClassRegisterHelper(){}
};// 提供反射类的注册宏,使用时仅提供基类类名和派生类类名
#define RegisterClass(base_class_name, sub_class_name) \static ClassRegisterHelper<base_class_name> \sub_class_name##_register_helper( \#sub_class_name, NewObject<base_class_name, sub_class_name>);// 创建对象的宏
#define CreateObject(base_class_name, sub_class_name_as_string) \GetRegister<base_class_name>().CreateObject(sub_class_name_as_string)#endif

下面是使用的示例:

#include <iostream>
#include <memory>
#include <cstring>
#include "base3.h"
using namespace std;class base
{public:base() {}virtual void test() { std::cout << "I'm base!" << std::endl; }virtual ~base() {}
};class A : public base
{public:A() { cout << " A constructor!" << endl; }virtual void test() { std::cout << "I'm A!" <<std::endl; }~A() { cout << " A destructor!" <<endl; }
};// 注册反射类 A
RegisterClass(base, A);class B : public base
{public :B() { cout << " B constructor!" << endl; }virtual void test() { std::cout << "I'm B!"; }~B() { cout << " B destructor!" <<endl; }
};// 注册反射类 B
RegisterClass(base, B);class base2
{public:base2() {}virtual void test() { std::cout << "I'm base2!" << std::endl; }virtual ~base2() {}
};class C : public base2
{public :C() { cout << " C constructor!" << endl; }virtual void test() { std::cout << "I'm C!" << std::endl; }~C(){ cout << " C destructor!" << endl; }
};// 注册反射类 C
RegisterClass(base2, C);int main()
{// 创建的时候提供基类和反射类的字符串类名base* p1 = CreateObject(base, "A");p1->test();delete p1;p1 = CreateObject(base, "B");p1->test();delete p1;base2* p2 = CreateObject(base2, "C");p2->test();delete p2;return 0;
}

3. parser解析源码,自动生成反射信息

要实现自动生成反射信息的功能,我们需要编写一个代码解析器,用于解析源代码并提取出需要的信息。一般来说,代码解析器会将源码转换为一棵抽象语法树(AST),然后对这棵树进行遍历,提取出需要的信息。

要使用Clang来解析源码并自动生成反射信息,可以借助Clang的AST(Abstract Syntax Tree)来实现。以下是一个简单的示例代码,演示如何使用Clang来解析源码并生成反射信息:

#include <iostream>
#include <string>
#include "clang/Tooling/Tooling.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/Tooling.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"using namespace clang;
using namespace clang::tooling;
using namespace clang::ast_matchers;class ReflectionGenerator : public MatchFinder::MatchCallback {
public:virtual void run(const MatchFinder::MatchResult &Result) {if (const CXXRecordDecl *Record = Result.Nodes.getNodeAs<CXXRecordDecl>("class")) {std::string className = Record->getNameAsString();std::cout << "Registering class: " << className << std::endl;// 在这里可以生成反射信息并注册类// 获取类名,并输出到控制台std::cout << "Class name: " << className << std::endl;// 遍历类的字段,并输出到控制台for (const FieldDecl *Field : Record->fields()) {std::string fieldName = Field->getNameAsString();std::cout << "Field name: " << fieldName << std::endl;}}}
};int main(int argc, const char **argv) {CommonOptionsParser OptionsParser(argc, argv);ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList());ReflectionGenerator Generator;MatchFinder Finder;Finder.addMatcher(cxxRecordDecl().bind("class"), &Generator);return Tool.run(newFrontendActionFactory(&Finder).get());
}

在这个示例代码中,我们通过Clang的AST来解析源码,并使用AST Matchers来匹配C++类的声明。当匹配到一个类声明时,ReflectionGenerator类的run方法会被调用,我们可以在这里生成反射信息并注册类。下面我们对上面代码中主要部分进行详细解释:

  1. ReflectionGenerator类:这是一个继承自MatchFinder::MatchCallback的自定义类,用于处理匹配到的AST节点。在run方法中,根据匹配到的C++类定义,获取类名并输出到控制台,同时遍历类的字段并输出字段名。

  2. main函数

    • 创建CommonOptionsParser对象和ClangTool对象,用于解析命令行参数和运行Clang工具。
    • 创建ReflectionGenerator对象和MatchFinder对象,用于注册匹配规则和处理匹配结果。
    • 通过Tool.run方法运行Clang工具,并传入匹配规则和处理结果的工厂对象。
  3. cxxRecordDecl matcher:使用Finder.addMatcher添加了一个匹配规则,用于匹配C++类的定义。当匹配到符合规则的AST节点时,会调用ReflectionGeneratorrun方法进行处理。

  4. 反射信息生成:在ReflectionGeneratorrun方法中,获取到类名和字段名后,输出到控制台。这里展示了获取类名和字段名的基本操作,您可以根据需求进一步扩展生成反射信息的逻辑。

此外这里我们可以通过attribute((annotate(…))) 来完成相同的操作。__attribute__((annotate(...))) 的意义是为代码中的类、字段、函数等元素添加自定义的元数据信息。这些信息可以用于实现反射、元编程、代码生成等功能。通过注解,我们可以为代码中的各种元素添加描述、标签、类型信息等,使其更具有可读性和可维护性,同时也可以在程序运行时动态地获取这些信息并进行相应的操作。在下面的示例中,我们使用了 __attribute__((annotate("reflect_class", "BarClass")))__attribute__((annotate("reflect_property", "int foo")))__attribute__((annotate("reflect_func", "void setFoo(int)")) 等注解来为类、字段和函数添加反射信息。这些注解可以帮助我们在编译时或运行时识别和操作这些元素,实现更高级的功能。

以下是一个完整的示例代码,其中使用了属性拓展和注解来实现反射功能:

#include <iostream>#define RFL_CLASS(...) __attribute__((annotate("reflect_class", #__VA_ARGS__)))
#define RFL_PROPERTY(...) __attribute__((annotate("reflect_property", #__VA_ARGS__)))
#define RFL_FUNC(...) __attribute__((annotate("reflect_func", #__VA_ARGS__)))class RFL_CLASS("BarClass") Bar {
public:RFL_PROPERTY("int foo") int foo;RFL_FUNC("void setFoo(int)") void setFoo(int value) {foo = value;}RFL_FUNC("int getFoo()") int getFoo() {return foo;}
};int main() {Bar bar;bar.setFoo(42);std::cout << "Value of foo: " << bar.getFoo() << std::endl;return 0;
}
  • RFL_CLASS 宏和类定义中,我们添加了一个字符串参数,用于指定类的名称。这样可以在反射时更准确地标识类。
  • RFL_PROPERTY 宏和字段定义中,我们添加了一个字符串参数,用于指定字段的类型和名称。这样可以在反射时更准确地标识字段。
  • RFL_FUNC 宏和成员函数定义中,我们添加了一个字符串参数,用于指定函数的签名。这样可以在反射时更准确地标识函数。

在main函数中,我们创建了一个Bar对象,并使用setFoo和getFoo函数来设置和获取foo字段的值。这些函数的签名和类/字段的信息都被注解添加到了代码中,以便在反射时能够准确地识别和访问它们。

3. 元信息结构反射机制

要在C++中构建一套可以在运行时使用的反射机制,你可以利用模板元编程在编译期生成类型元信息,并在运行时通过这些元信息进行反射操作。

首先,我们需要定义一种数据结构来存储类型的元信息。

#include <iostream>
#include <string>
#include <typeinfo>
#include <typeindex>
#include <unordered_map>
#include <vector>struct FieldInfo {std::string name;std::type_index type;size_t offset;
};struct TypeInfo {std::string name;std::vector<FieldInfo> fields;
};

然后,我们需要一个机制来自动生成这些元信息。这里我们用模板和宏来实现这一点。

#define REFLECTABLE(...) \friend struct Reflection; \static void reflect(Reflection& r) { \r.registerType(typeid(*this), #__VA_ARGS__, __VA_ARGS__); \}class Reflection {
public:template<typename T>void registerType(std::type_index type, const std::string& fieldNames, T& instance) {std::istringstream stream(fieldNames);std::string fieldName;size_t offset = 0;while (std::getline(stream, fieldName, ',')) {trim(fieldName);TypeInfo& typeInfo = typeRegistry[type];typeInfo.name = type.name();typeInfo.fields.push_back({ fieldName, typeid(instance. * (T::*)(T:: *) &fieldName), offset });offset += sizeof(fieldName);}}template<typename T>const TypeInfo& getTypeInfo() {return typeRegistry[std::type_index(typeid(T))];}private:std::unordered_map<std::type_index, TypeInfo> typeRegistry;void trim(std::string& s) {s.erase(0, s.find_first_not_of(' '));s.erase(s.find_last_not_of(' ') + 1);}
};

现在,我们可以定义一个类,并使用宏来使其成为可反射的。

class MyClass {
public:int x;float y;std::string z;REFLECTABLE(x, y, z)
};

4. 使用反射机制

最后,我们可以在运行时使用反射机制来访问类的元信息。

int main() {MyClass obj;Reflection reflection;// 注册类型信息obj.reflect(reflection);// 获取类型信息const TypeInfo& typeInfo = reflection.getTypeInfo<MyClass>();// 输出字段信息std::cout << "Type: " << typeInfo.name << std::endl;for (const auto& field : typeInfo.fields) {std::cout << "Field: " << field.name << ", Type: " << field.type.name() << ", Offset: " << field.offset << std::endl;}return 0;
}
  1. FieldInfo 和 TypeInfo 结构体:这些结构体用于存储字段和类型的元信息。
  2. Reflection 类:这个类负责注册和存储类型元信息,并提供查询接口。
  3. REFLECTABLE 宏:这个宏用于简化类型元信息的注册过程。它声明一个友元函数,这个函数可以访问类的私有成员,并在编译期生成字段的名字和类型信息。
  4. registerType 和 getTypeInfo 方法
    • registerType 方法在编译期处理传入的字段名称,生成对应的字段信息并存储在一个哈希表中。
    • getTypeInfo 方法在运行时查询并返回存储的类型信息。
  5. MyClass 类:这是一个简单的示例类,通过 REFLECTABLE 宏声明它是可反射的。
  6. main 函数:在 main 函数中,我们创建一个 MyClass 的实例并注册它的类型信息,然后查询并输出这些信息。

这个例子展示了如何在 C++ 中利用模板和宏实现一个简单的反射机制。这个机制允许你在运行时访问类型的元信息,从而实现各种动态操作,例如序列化和反序列化、类型检查和动态调用等等。

5. 调试器的运行时信息形成反射

使用调试器的运行时信息来生成反射代码是一种非常高级的技术,通常需要结合调试器提供的 API 和脚本语言(例如 Python)进行自动化处理。这个过程大致可以分为以下几个步骤:

  1. 使用调试器获取目标程序的运行时信息
  2. 解析并提取元信息
  3. 生成对应的反射代码

首先,编写一个简单的 C++ 程序:

// example.cpp
#include <iostream>
#include <vector>
#include <string>class MyClass {
public:int x;float y;std::string z;void print() {std::cout << "x: " << x << ", y: " << y << ", z: " << z << std::endl;}
};int main() {MyClass obj;obj.x = 10;obj.y = 20.5f;obj.z = "Hello, World!";obj.print();return 0;
}

编译这个程序:

g++ -g example.cpp -o example

…详情请参照古月居

相关文章:

在C++上实现反射用法

0. 简介 最近看很多端到端的工作&#xff0c;发现大多数都是基于mmdet3d来做的&#xff0c;而这个里面用的比较多的形式就是反射机制&#xff0c;这样其实可以比较好的通过类似plugin的形式完成模型模块的插入。当然我们这里不是来分析python的反射机制的。我们这篇文章主要来…...

【学术会议介绍,SPIE 出版】第四届计算机图形学、人工智能与数据处理国际学术会议 (ICCAID 2024,12月13-15日)

第四届计算机图形学、人工智能与数据处理国际学术会议 2024 4th International Conference on Computer Graphics, Artificial Intelligence and Data Processing (ICCAID 2024) 重要信息 大会官网&#xff1a;www.iccaid.net 大会时间&#xff1a;2024年12月13-15日 大会地…...

网络百问百答(一)

什么是链接&#xff1f; 链接是指两个设备之间的连接&#xff0c;它包括用于一个设备能够与另一个设备通信的电缆类型和协议。OSI参考模型的层次是什么&#xff1f; 有7个OSI层&#xff1a;物理层&#xff0c;数据链路层&#xff0c;网络层&#xff0c;传输层&#xff0c;会话层…...

【深圳大学】数据结构A+攻略(计软版)

1. 考试 1.1 形式 分为平时&#xff0c;笔试&#xff0c;机试三部分。其中&#xff1a; 平时占30%&#xff0c;包含平时OJ测验和课堂练习&#xff0c;注意这个可能会因老师的不同和课题组的新策略而改变。笔试占60%&#xff0c;是分值占比的主要部分。机试占10%。 1.2 题型…...

解读《ARM Cortex-M3 与Cortex-M4 权威指南》——第4章 架构

推荐大佬做的讲解 可以帮助加深理解 ARM架构及汇编 Cortex-M3 和 Cortex-M4 处理器都是基于ARMv7-M架构 需要完成对编程模型、异常(如中断)如何处理、存储器映射、如何使用外设以及如何使用微控制器供应商提供的软件驱动库文件等 Cortex-M3和Cortex-M4处理器有两种操作状态…...

探索 Python HTTP 的瑞士军刀:Requests 库

文章目录 探索 Python HTTP 的瑞士军刀&#xff1a;Requests 库第一部分&#xff1a;背景介绍第二部分&#xff1a;Requests 库是什么&#xff1f;第三部分&#xff1a;如何安装 Requests 库&#xff1f;第四部分&#xff1a;Requests 库的基本函数使用方法第五部分&#xff1a…...

PostgreSQL 页损坏如何修复

PostgreSQL 错误&#xff1a;关系 base/46501/52712 中的块 480 存在无效的页。 当我们在使用 PostgreSQL 数据库的时候&#xff0c;如果服务器发生 CRASH 或者断电等异常情况的时候&#xff0c;有可能会遇到上面的这个报错信息。那么我们如何去修复这个数据呢&#xff0c;以及…...

Leetcode 75 Sort colors

题意&#xff1a;荷兰国旗问题&#xff0c;给一个数组[0,0,2,1,0]&#xff0c;构造成[0,0,0,1,2]的形式&#xff0c;分成三块 https://leetcode.com/problems/sort-colors/description/ 题解&#xff1a; 在任意时刻&#xff0c;i 左边的数都是 0&#xff0c;k 右边的数都是 …...

如何进行数据库连接池的参数优化?

以下是进行数据库连接池参数优化的一些方法&#xff1a; 一、确定合适的初始连接数&#xff1a; 考虑因素&#xff1a;数据库的规模、应用程序的启动需求以及预期的初始负载。如果数据库规模较小且应用程序启动时对数据库的即时访问需求不高&#xff0c;可以将初始连接数设置…...

有了miniconda,再也不用担心python、nodejs、go的版本问题了

哈喽&#xff0c;大家好&#xff01;我是「励志前端小黑哥」&#xff0c;我带着最新发布的文章又来了&#xff01; 专注前端领域10年&#xff0c;专门分享那些没用的前端知识&#xff01; 今天要分享的内容&#xff0c;是一个免费的环境管理工具&#xff0c;它叫Miniconda&…...

openresty入门教程:init_by_lua_block

init_by_lua_block 是 Nginx 配置中用于在 Nginx 启动时执行 Lua 脚本的一个指令。这个指令通常用于初始化全局变量、设置共享内存&#xff0c;或者执行一些需要在服务器启动时完成的准备工作。 以下是一个简单的 init_by_lua_block 使用示例&#xff1a; 1. 安装 Nginx 和 L…...

sol机器人pump机器人如何实现盈利的?什么是Pump 扫链机器人?

什么是Pump 扫链机器人&#xff0c;它的盈利逻辑优化策略是什么&#xff1f; Pump 扫链机器人&#xff0c;通过智能化、自动化的买卖操作帮助投资者实现快速盈利。在此基础上&#xff0c;我们对该机器人的盈利逻辑进行了深度优化&#xff0c;涵盖了买入策略和止盈策略的各个方面…...

Spring-boot 后端java配置接口返回jsp页面

Spring-boot 后端java配置接口返回jsp页面 spring boot 基于spring MVC的基础上进行了改进&#xff0c; 将Controller 与ResponseBody 进行了合并成一个新的注解 RestController。 当用户请求时&#xff0c;需要有视图渲染的&#xff0c;与请求数据的请求分别使用 1.在appli…...

LabVIEW车辆侧翻预警系统

在工业和实验室环境中&#xff0c;搬运车辆、叉车和特种作业车辆经常在负载和高速转弯过程中发生侧翻事故&#xff0c;导致设备损坏和人员伤害。为提高工作环境的安全性&#xff0c;开发了一种基于LabVIEW的工业车辆侧翻预警系统&#xff0c;能够实时监测车辆状态并发出预警&am…...

亲测有效:Maven3.8.1使用Tomcat8插件启动项目

我本地maven的settings.xml文件中的配置&#xff1a; <mirror><id>aliyunmaven</id><mirrorOf>central</mirrorOf><name>阿里云公共仓库</name><url>https://maven.aliyun.com/repository/public</url> </mirror>…...

Find My电子体温计|苹果Find My技术与体温计结合,智能防丢,全球定位

电子体温计由温度传感器&#xff0c;液晶显示器&#xff0c;纽扣电池&#xff0c;专用集成电路及其他电子元器件组成。能快速准确地测量人体体温&#xff0c;与传统的水银玻璃体温计相比&#xff0c;具有读数方便&#xff0c;测量时间短&#xff0c;测量精度高&#xff0c;能记…...

jmeter常用配置元件介绍总结之后置处理器

系列文章目录 安装jmeter jmeter常用配置元件介绍总结之后置处理器 8.后置处理器8.1.CSS/JQuery提取器8.2.JSON JMESPath Extractor8.3.JSON提取器8.4.正则表达式提取器8.5.边界提取器8.5.Debug PostProcessor8.6.XPath2 Extractor8.7.XPath提取器8.8.结果状态处理器 8.后置处理…...

html5多媒体标签

文章目录 HTML5新增多媒体标签详解&#xff1a;视频标签与音频标签视频标签<video>音频标签<audio>代码案例 HTML5新增多媒体标签详解&#xff1a;视频标签与音频标签 HTML5引入了多项新特性&#xff0c;其中多媒体标签的引入为网页开发带来了革命性的变化。这些标…...

51c自动驾驶~合集10

我自己的原文哦~ https://blog.51cto.com/whaosoft/11638131 #端到端任务 说起端到端&#xff0c;每个从业者可能都觉得会是下一代自动驾驶量产方案绕不开的点&#xff01;特斯拉率先吹响了方案更新的号角&#xff0c;无论是完全端到端&#xff0c;还是专注于planner的模型&a…...

JAVA学习日记(十五) 数据结构

一、数据结构概述 数据结构是计算机底层存储、组织数据的方式。 数据结构是指数据相互之间以什么方式排列在一起的。 数据结构是为了更加方便的管理和使用数据&#xff0c;需要结合具体的业务场景来进行选择。 二、常见的数据结构 &#xff08;一&#xff09;栈 特点&…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...