Env类的声明:
class Env {
public:
typedef RWMutex RWMutexType;
/**
* @brief 初始化,包括记录程序名称与路径,解析命令行选项和参数
* @details 命令行选项全部以-开头,后面跟可选参数,选项与参数构成key-value结构,被存储到程序的自定义环境变量中,
* 如果只有key没有value,那么value为空字符串
* @param[in] argc main函数传入
* @param[in] argv main函数传入
* @return 是否成功
*/
bool init(int argc, char **argv);
/**
* @brief 添加自定义环境变量,存储在程序内部的map结构中
*/
void add(const std::string &key, const std::string &val);
/**
* @brief 获取是否存在键值为key的自定义环境变量
*/
bool has(const std::string &key);
/**
* @brief 删除键值为key的自定义环境变量
*/
void del(const std::string &key);
/**
* @brief 获键值为key的自定义环境变量,如果未找到,则返回提供的默认值
*/
std::string get(const std::string &key, const std::string &default_value = "");
/**
* @brief 增加命令行帮助选项
* @param[in] key 选项名
* @param[in] desc 选项描述
*/
void addHelp(const std::string &key, const std::string &desc);
/**
* @brief 删除命令行帮助选项
* @param[in] key 选项名
*/
void removeHelp(const std::string &key);
/**
* @brief 打印帮助信息
*/
void printHelp();
/**
* @brief 获取exe完整路径,从/proc/$pid/exe的软链接路径中获取,参考readlink(2)
*/
const std::string &getExe() const { return m_exe; }
/**
* @brief 获取当前路径,从main函数的argv[0]中获取,以/结尾
* @return
*/
const std::string &getCwd() const { return m_cwd; }
/**
* @brief 设置系统环境变量,参考setenv(3)
*/
bool setEnv(const std::string &key, const std::string &val);
/**
* @brief 获取系统环境变量,参考getenv(3)
*/
std::string getEnv(const std::string &key, const std::string &default_value = "");
/**
* @brief 提供一个相对当前的路径path,返回这个path的绝对路径
* @details 如果提供的path以/开头,那直接返回path即可,否则返回getCwd()+path
*/
std::string getAbsolutePath(const std::string &path) const;
/**
* @brief 获取工作路径下path的绝对路径
*/
std::string getAbsoluteWorkPath(const std::string& path) const;
/**
* @brief 获取配置文件路径,配置文件路径通过命令行-c选项指定,默认为当前目录下的conf文件夹
*/
std::string getConfigPath();
private:
/// Mutex
RWMutexType m_mutex;
/// 存储程序的自定义环境变量
std::map<std::string, std::string> m_args;
/// 存储帮助选项与描述
std::vector<std::pair<std::string, std::string>> m_helps;
/// 程序名,也就是argv[0]
std::string m_program;
/// 程序完整路径名,也就是/proc/$pid/exe软链接指定的路径
std::string m_exe;
/// 当前路径,从argv[0]中获取
std::string m_cwd;
};
各函数讲解
这个模块用于环境变量管理接口,利用单例模式只有一个实例化的对象。
Env类无构造函数。
Env类内用map存储程序自定义的环境变量m_args和帮助选项和描述m_helps,还存储了字符串形式的m_program(程序名), m_exe(程序完整路径名), m_cwd(当前路径)。
Env类内通过init初始化类内五个成员变量,传入main函数接收的两个参数argc和argv。argc是main接收参数的数目,argv是一个指向字符串数组的指针。函数内首先获取从指定进程号目录下获取一个符号链接文件(比如假如服务器进程是114514,符号链接文件的路径则是/proc/114514/exe),然后调用readlink函数,从这个符号链接文件中获取当前运行的程序的文件绝对路径,并存取m_exe中,然后取出当前路径存入m_cwd,从argv[0]中取出m_program并存入。然后通过"-"来区分是选项还是选项值。调用add函数添加环境变量,存储到map中。成功则返回true,解析失败则返回false。
bool Env::init(int argc, char **argv) {
char link[1024] = {0};
char path[1024] = {0};
sprintf(link, "/proc/%d/exe", getpid());
readlink(link, path, sizeof(path));
// /path/xxx/exe
m_exe = path;
auto pos = m_exe.find_last_of("/");
m_cwd = m_exe.substr(0, pos) + "/";
m_program = argv[0];
// -config /path/to/config -file xxxx -d
const char *now_key = nullptr;
for (int i = 1; i < argc; ++i) {
if (argv[i][0] == '-') {
if (strlen(argv[i]) > 1) {
if (now_key) {
add(now_key, "");
}
now_key = argv[i] + 1;
} else {
SYLAR_LOG_ERROR(g_logger) << "invalid arg idx=" << i
<< " val=" << argv[i];
return false;
}
} else {
if (now_key) {
add(now_key, argv[i]);
now_key = nullptr;
} else {
SYLAR_LOG_ERROR(g_logger) << "invalid arg idx=" << i
<< " val=" << argv[i];
return false;
}
}
}
if (now_key) {
add(now_key, "");
}
return true;
}
add函数用于添加自定义的环境变量。、
has函数用于判断是否存在该选项,并返回true或false。
del函数用于删除指定选项的的环境变量。
get函数用于获取指定选项的自定义环境变量,若不存在则返回默认值(约定优于配置)。
addHelp函数用于为指定选项添加帮助选项。
removeHelp函数用于删除指定选项的帮助选项。
printHelp函数用于打印帮助信息。
getExe/getCwd用于获取完整路径和当前路径。
setEnv/getEnv用于设置和获取环境变量,获取环境变量若为空,则返回默认值。
getAbsolutePath需要传入一个相对路径,然后返回绝对路径。
getAbsoluteWorkPath传入一个相对路径,返回工作路径下的绝对路径(查看配置选项server.work_path获取工作路径)。
getConfigPath返回配置文件路径,其实就是返回-c的选项值,默认为conf
一些细节:
1.环境变量管理接口类不需要用智能指针管理(程序运行时必须有且只有一个实例)。
2.环境变量管理接口类也是通过单例模式管理的。
3.为什么不使用构造函数传入argc和argv并实例化Env类?
这个服务器框架的单例模式是通过函数内的静态变量在函数执行结束的时候不会释放内存实现的,框架缺陷,所以没法写。
4.Env类内大量使用读写锁