easylogging++的那些事(四)源码分析(十四)其他工具类(二)
目录
在 其他工具类一 中我们介绍了部分工具类,今天我们继续看看其他一些工具类。
NoCopy 类
NoCopy
类主要用于防止对象被复制。/// @detail When using this class simply inherit it privately class NoCopy { protected: NoCopy(void) {} private: NoCopy(const NoCopy &); NoCopy &operator=(const NoCopy &); };
禁用
COPY
语义并且只能被继承,不能直接构建NoCopy
实例,源码当中用于私有继承,达到禁用copy
语义的目的。
StaticClass 类
StaticClass
类主要用于作为所有工具类的私有基类,工具类仅仅包含静态方法, 不能构建对象,正如其名。/// @brief Internal helper class that makes all default constructors private. /// /// @detail This prevents initializing class making it static unless an explicit constructor is declared. /// When using this class simply inherit it privately class StaticClass { private: StaticClass(void); StaticClass(const StaticClass &); StaticClass &operator=(const StaticClass &); };
LevelHelper 类
LevelHelper
类是el::Level
的工具类。/// @brief Static class that contains helper functions for el::Level class LevelHelper : base::StaticClass { public: // 最小有效分层日志级别的常量定义 /// @brief Represents minimum valid level. Useful when iterating through enum. static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(Level::Trace); // 最大有效分层日志级别的常量定义 /// @brief Represents maximum valid level. This is used internally and you should not need it. static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(Level::Info); // Level转EnumType /// @brief Casts level to int, useful for iterating through enum. static base::type::EnumType castToInt(Level level) { return static_cast<base::type::EnumType>(level); } // EnumType转Level /// @brief Casts int(ushort) to level, useful for iterating through enum. static Level castFromInt(base::type::EnumType l) { return static_cast<Level>(l); } // 返回分层日志级别的字符串表示形式 /// @brief Converts level to associated const char* /// @return Upper case string based level. static const char *convertToString(Level level); // 将字符串形式的分层日志级别转为Level /// @brief Converts from levelStr to Level /// @param levelStr Upper case string based level. /// Lower case is also valid but providing upper case is recommended. static Level convertFromString(const char *levelStr); // 从指定分层日志级别开始遍历,对于每个级别执行一些操作(fn),直到fn返回true为止。 /// @brief Applies specified function to each level starting from startIndex /// @param startIndex initial value to start the iteration from. This is passed as pointer and /// is left-shifted so this can be used inside function (fn) to represent current level. /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels. static void forEachLevel(base::type::EnumType *startIndex, const std::function<bool(void)> &fn); }; // 返回分层日志级别的字符串表示形式 const char *LevelHelper::convertToString(Level level) { // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. if (level == Level::Global) return "GLOBAL"; if (level == Level::Debug) return "DEBUG"; if (level == Level::Info) return "INFO"; if (level == Level::Warning) return "WARNING"; if (level == Level::Error) return "ERROR"; if (level == Level::Fatal) return "FATAL"; if (level == Level::Verbose) return "VERBOSE"; if (level == Level::Trace) return "TRACE"; return "UNKNOWN"; } struct StringToLevelItem { const char *levelString; Level level; }; static struct StringToLevelItem stringToLevelMap[] = { {"global", Level::Global}, {"debug", Level::Debug}, {"info", Level::Info}, {"warning", Level::Warning}, {"error", Level::Error}, {"fatal", Level::Fatal}, {"verbose", Level::Verbose}, {"trace", Level::Trace}}; // 将字符串形式的分层日志级别转为Level类型 Level LevelHelper::convertFromString(const char *levelStr) { for (auto &item : stringToLevelMap) { if (base::utils::Str::cStringCaseEq(levelStr, item.levelString)) { return item.level; } } return Level::Unknown; } // 从指定分层日志级别开始遍历,对于每个级别执行一些操作(fn),直到fn返回true为止。 void LevelHelper::forEachLevel(base::type::EnumType *startIndex, const std::function<bool(void)> &fn) { base::type::EnumType lIndexMax = LevelHelper::kMaxValid; do { if (fn()) { break; } *startIndex = static_cast<base::type::EnumType>(*startIndex << 1); } while (*startIndex <= lIndexMax); }
ConfigurationTypeHelper 类
ConfigurationTypeHelper
类是el::ConfigurationType
的工具类。/// @brief Static class that contains helper functions for el::ConfigurationType class ConfigurationTypeHelper : base::StaticClass { public: // 最小有效ConfigurationType的常量定义 /// @brief Represents minimum valid configuration type. Useful when iterating through enum. static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(ConfigurationType::Enabled); // 最大有效ConfigurationType的常量定义 /// @brief Represents maximum valid configuration type. This is used internally and you should not need it. static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(ConfigurationType::MaxLogFileSize); // ConfigurationType转EnumType /// @brief Casts configuration type to int, useful for iterating through enum. static base::type::EnumType castToInt(ConfigurationType configurationType) { return static_cast<base::type::EnumType>(configurationType); } // EnumType转ConfigurationType /// @brief Casts int(ushort) to configuration type, useful for iterating through enum. static ConfigurationType castFromInt(base::type::EnumType c) { return static_cast<ConfigurationType>(c); } /// @brief Converts configuration type to associated const char* /// @returns Upper case string based configuration type. static const char *convertToString(ConfigurationType configurationType); /// @brief Converts from configStr to ConfigurationType /// @param configStr Upper case string based configuration type. /// Lower case is also valid but providing upper case is recommended. static ConfigurationType convertFromString(const char *configStr); /// @brief Applies specified function to each configuration type starting from startIndex /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted /// so this can be used inside function (fn) to represent current configuration type. /// @param fn function to apply with each configuration type. /// This bool represent whether or not to stop iterating through configurations. static inline void forEachConfigType(base::type::EnumType *startIndex, const std::function<bool(void)> &fn); }; const char *ConfigurationTypeHelper::convertToString(ConfigurationType configurationType) { // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. if (configurationType == ConfigurationType::Enabled) return "ENABLED"; if (configurationType == ConfigurationType::Filename) return "FILENAME"; if (configurationType == ConfigurationType::Format) return "FORMAT"; if (configurationType == ConfigurationType::ToFile) return "TO_FILE"; if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT"; if (configurationType == ConfigurationType::SubsecondPrecision) return "SUBSECOND_PRECISION"; if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING"; if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE"; if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD"; return "UNKNOWN"; } struct ConfigurationStringToTypeItem { const char *configString; ConfigurationType configType; }; static struct ConfigurationStringToTypeItem configStringToTypeMap[] = { {"enabled", ConfigurationType::Enabled}, {"to_file", ConfigurationType::ToFile}, {"to_standard_output", ConfigurationType::ToStandardOutput}, {"format", ConfigurationType::Format}, {"filename", ConfigurationType::Filename}, {"subsecond_precision", ConfigurationType::SubsecondPrecision}, {"milliseconds_width", ConfigurationType::MillisecondsWidth}, {"performance_tracking", ConfigurationType::PerformanceTracking}, {"max_log_file_size", ConfigurationType::MaxLogFileSize}, {"log_flush_threshold", ConfigurationType::LogFlushThreshold}, }; ConfigurationType ConfigurationTypeHelper::convertFromString(const char *configStr) { for (auto &item : configStringToTypeMap) { if (base::utils::Str::cStringCaseEq(configStr, item.configString)) { return item.configType; } } return ConfigurationType::Unknown; } // 从指定ConfigurationType开始遍历,对于每个ConfigurationType执行一些操作的接口(fn),直到fn返回true为止。 void ConfigurationTypeHelper::forEachConfigType(base::type::EnumType *startIndex, const std::function<bool(void)> &fn) { base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid; do { if (fn()) { break; } *startIndex = static_cast<base::type::EnumType>(*startIndex << 1); } while (*startIndex <= cIndexMax); }
safeDelete 模板接口
safeDelete
函数模板用于安全释放指针/// @brief Deletes memory safely and points to null template <typename T> static typename std::enable_if<std::is_pointer<T *>::value, void>::type safeDelete(T *&pointer) { if (pointer == nullptr) return; delete pointer; pointer = nullptr; }
运用了
SFINAE
模板技巧,enable_if
模板可以用它来选择性地启用某个函数的重载。
如果T*
是指针的话,最终才会实例化这个函数模板,此时typename std::enable_if<std::is_pointer<T*>::value, void >::type
的值为 void。
不清楚的同学,可以查看 C++标准库的文档,这里就不多介绍了。
位运算
/// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation /// Use these function as <pre>flag = bitwise::Or<MyEnum>(MyEnum::val1, flag);</pre> namespace bitwise { template <typename Enum> static inline base::type::EnumType And(Enum e, base::type::EnumType flag) { return static_cast<base::type::EnumType>(flag) & static_cast<base::type::EnumType>(e); } template <typename Enum> static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) { return static_cast<base::type::EnumType>(flag) & ~(static_cast<base::type::EnumType>(e)); } template <typename Enum> static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) { return static_cast<base::type::EnumType>(flag) | static_cast<base::type::EnumType>(e); } } // namespace bitwise template <typename Enum> static inline void addFlag(Enum e, base::type::EnumType *flag) { *flag = base::utils::bitwise::Or<Enum>(e, *flag); } template <typename Enum> static inline void removeFlag(Enum e, base::type::EnumType *flag) { *flag = base::utils::bitwise::Not<Enum>(e, *flag); } template <typename Enum> static inline bool hasFlag(Enum e, base::type::EnumType flag) { return base::utils::bitwise::And<Enum>(e, flag) > 0x0; }
File 类
File
类是跨平台文件操作类class File : base::StaticClass { public: // 根据指定的文件名打开一个新的文件流 /// @brief Creates new out file stream for specified filename. /// @return Pointer to newly created fstream or nullptr static base::type::fstream_t *newFileStream(const std::string &filename); // 获取文件的大小 /// @brief Gets size of file provided in stream static std::size_t getSizeOfFile(base::type::fstream_t *fs); // 指定路径对应的文件是否存在 /// @brief Determines whether or not provided path exist in current file system static bool pathExists(const char *path, bool considerFile = false); // 递归创建文件目录 /// @brief Creates specified path on file system /// @param path Path to create. static bool createPath(const std::string &path); // 获取文件的目录路径 /// @brief Extracts path of filename with leading slash static std::string extractPathFromFilename(const std::string &fullPath, const char *separator = base::consts::kFilePathSeparator); // 调整文件名(仅仅只有文件名,不带路径),文件名过长超过缓冲区的大小时,文件名调整为以..开头+文件名剩余部分使其能够放入指定大小的缓冲区 /// @brief builds stripped filename and puts it in buff static void buildStrippedFilename(const char *filename, char buff[], std::size_t limit = base::consts::kSourceFilenameMaxLength); // 和buildStrippedFilename功能一样,区别是参数为全路径文件名 /// @brief builds base filename and puts it in buff static void buildBaseFilename(const std::string &fullPath, char buff[], std::size_t limit = base::consts::kSourceFilenameMaxLength, const char *separator = base::consts::kFilePathSeparator); }; // 根据指定的文件名打开一个新的文件流 base::type::fstream_t *File::newFileStream(const std::string &filename) { base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(), base::type::fstream_t::out #if !defined(ELPP_FRESH_LOG_FILE) | base::type::fstream_t::app #endif ); #if defined(ELPP_UNICODE) std::locale elppUnicodeLocale(""); #if ELPP_OS_WINDOWS std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16<wchar_t>); elppUnicodeLocale = elppUnicodeLocaleWindows; #endif // ELPP_OS_WINDOWS fs->imbue(elppUnicodeLocale); #endif // defined(ELPP_UNICODE) if (fs->is_open()) { fs->flush(); } else { base::utils::safeDelete(fs); ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true); } return fs; } // 获取文件的大小 std::size_t File::getSizeOfFile(base::type::fstream_t *fs) { if (fs == nullptr) { return 0; } // Since the file stream is appended to or truncated, the current // offset is the file size. std::size_t size = static_cast<std::size_t>(fs->tellg()); return size; } // 指定路径对应的文件是否存在 bool File::pathExists(const char *path, bool considerFile) { if (path == nullptr) { return false; } #if ELPP_OS_UNIX ELPP_UNUSED(considerFile); struct stat st; return (stat(path, &st) == 0); #elif ELPP_OS_WINDOWS DWORD fileType = GetFileAttributesA(path); if (fileType == INVALID_FILE_ATTRIBUTES) { return false; } return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true); #endif // ELPP_OS_UNIX } // 递归创建文件目录 bool File::createPath(const std::string &path) { if (path.empty()) { return false; } if (base::utils::File::pathExists(path.c_str())) { return true; } int status = -1; char *currPath = const_cast<char *>(path.c_str()); std::string builtPath = std::string(); #if ELPP_OS_UNIX if (path[0] == '/') { builtPath = "/"; } currPath = STRTOK(currPath, base::consts::kFilePathSeparator, 0); #elif ELPP_OS_WINDOWS // Use secure functions API char *nextTok_ = nullptr; currPath = STRTOK(currPath, base::consts::kFilePathSeparator, &nextTok_); ELPP_UNUSED(nextTok_); #endif // ELPP_OS_UNIX while (currPath != nullptr) { builtPath.append(currPath); builtPath.append(base::consts::kFilePathSeparator); #if ELPP_OS_UNIX status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS); currPath = STRTOK(nullptr, base::consts::kFilePathSeparator, 0); #elif ELPP_OS_WINDOWS status = _mkdir(builtPath.c_str()); currPath = STRTOK(nullptr, base::consts::kFilePathSeparator, &nextTok_); #endif // ELPP_OS_UNIX } if (status == -1) { ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true); return false; } return true; } // 获取文件的目录路径 std::string File::extractPathFromFilename(const std::string &fullPath, const char *separator) { if ((fullPath == "") || (fullPath.find(separator) == std::string::npos)) { return fullPath; } std::size_t lastSlashAt = fullPath.find_last_of(separator); if (lastSlashAt == 0) { return std::string(separator); } return fullPath.substr(0, lastSlashAt + 1); } // 调整文件名(仅仅只有文件名,不带路径),文件名过长超过缓冲区的大小时,文件名调整为以..开头+文件名剩余部分使其能够放入指定大小的缓冲区 void File::buildStrippedFilename(const char *filename, char buff[], std::size_t limit) { std::size_t sizeOfFilename = strlen(filename); if (sizeOfFilename >= limit) { filename += (sizeOfFilename - limit); if (filename[0] != '.' && filename[1] != '.') { // prepend if not already filename += 3; // 3 = '..' STRCAT(buff, "..", limit); } } STRCAT(buff, filename, limit); } // 和buildStrippedFilename功能一样,区别是参数为全路径文件名 void File::buildBaseFilename(const std::string &fullPath, char buff[], std::size_t limit, const char *separator) { const char *filename = fullPath.c_str(); std::size_t lastSlashAt = fullPath.find_last_of(separator); filename += lastSlashAt ? lastSlashAt + 1 : 0; std::size_t sizeOfFilename = strlen(filename); if (sizeOfFilename >= limit) { filename += (sizeOfFilename - limit); if (filename[0] != '.' && filename[1] != '.') { // prepend if not already filename += 3; // 3 = '..' STRCAT(buff, "..", limit); } } STRCAT(buff, filename, limit); }
Str 类
Str
类是跨平台字符串操作工具类/// @brief String utilities helper class used internally. You should not use it. class Str : base::StaticClass { public: // 字符是否为数字 /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues. static inline bool isDigit(char c) { return c >= '0' && c <= '9'; } // 字符串匹配,仅仅支持*和? /// @brief Matches wildcards, '*' and '?' only supported. static bool wildCardMatch(const char *str, const char *pattern); // 去除字符串左边的空白字符 static std::string <rim(std::string &str); // 去除字符串右边边的空白字符 static std::string &rtrim(std::string &str); // 去除字符串左右两边的空白字符 static std::string &trim(std::string &str); // 检查字符串是否以start子串开始 /// @brief Determines whether or not str starts with specified string /// @param str String to check /// @param start String to check against /// @return Returns true if starts with specified string, false otherwise static bool startsWith(const std::string &str, const std::string &start); // 检查字符串是否以end子串结束 /// @brief Determines whether or not str ends with specified string /// @param str String to check /// @param end String to check against /// @return Returns true if ends with specified string, false otherwise static bool endsWith(const std::string &str, const std::string &end); // 替换所有指定的字符 /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance. /// @param [in,out] str String to replace from /// @param replaceWhat Character to replace /// @param replaceWith Character to replace with /// @return Modified version of str static std::string &replaceAll(std::string &str, char replaceWhat, char replaceWith); // 替换所有指定的字符串 /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place /// @param str String to replace from /// @param replaceWhat Character to replace /// @param replaceWith Character to replace with /// @return Modified (original) str static std::string &replaceAll(std::string &str, const std::string &replaceWhat, const std::string &replaceWith); // 替换指定的字符串 // 功能介绍:如果目标子串的前一个字符是'%',就仅仅去掉前面的%,然后继续查找,反之,当遇到第一个前面不是‘%’的目标子串时,进行替换,然后替换结束 static void replaceFirstWithEscape(base::type::string_t &str, const base::type::string_t &replaceWhat, const base::type::string_t &replaceWith); #if defined(ELPP_UNICODE) // unicode版本 调用普通版本 static void replaceFirstWithEscape(base::type::string_t &str, const base::type::string_t &replaceWhat, const std::string &replaceWith); #endif // defined(ELPP_UNICODE) // 字符串转为大写 /// @brief Converts string to uppercase /// @param str String to convert /// @return Uppercase string static std::string &toUpper(std::string &str); // 字符串相等比较(区分大小写) /// @brief Compares cstring equality - uses strcmp static bool cStringEq(const char *s1, const char *s2); // 字符串相等比较(不区分大小写) /// @brief Compares cstring equality (case-insensitive) - uses toupper(char) /// Dont use strcasecmp because of CRT (VC++) static bool cStringCaseEq(const char *s1, const char *s2); // 判断字符串当中是否含有某个字符 /// @brief Returns true if c exist in str static bool contains(const char *str, char c); // 就是用于将数字转字符,推荐做法是使用printf函数家族,也会不存在性能问题。 static char *convertAndAddToBuff(std::size_t n, int len, char *buf, const char *bufLim, bool zeroPadded = true); // 将字符串的内容添加到某个缓冲区中 static char *addToBuff(const char *str, char *buf, const char *bufLim); // 缓冲区清空 static char *clearBuff(char buff[], std::size_t lim); // wchar*转为char* /// @brief Converts wchar* to char* /// NOTE: Need to free return value after use! static char *wcharPtrToCharPtr(const wchar_t *line); }; // 字符串匹配,仅仅支持*和? bool Str::wildCardMatch(const char *str, const char *pattern) { while (*pattern) { switch (*pattern) { case '?': if (!*str) return false; ++str; ++pattern; break; case '*': if (wildCardMatch(str, pattern + 1)) return true; if (*str && wildCardMatch(str + 1, pattern)) return true; return false; default: if (*str++ != *pattern++) return false; break; } } return !*str && !*pattern; } // 去除字符串左边的空白字符 std::string &Str::ltrim(std::string &str) { str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](char c) { return !std::isspace(c); })); return str; } // 去除字符串右边边的空白字符 std::string &Str::rtrim(std::string &str) { str.erase(std::find_if(str.rbegin(), str.rend(), [](char c) { return !std::isspace(c); }) .base(), str.end()); return str; } // 去除字符串左右两边的空白字符 std::string &Str::trim(std::string &str) { return ltrim(rtrim(str)); } // 检查字符串是否以start子串开始 bool Str::startsWith(const std::string &str, const std::string &start) { return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0); } // 检查字符串是否以end子串结束 bool Str::endsWith(const std::string &str, const std::string &end) { return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0); } // 替换所有指定的字符 std::string &Str::replaceAll(std::string &str, char replaceWhat, char replaceWith) { std::replace(str.begin(), str.end(), replaceWhat, replaceWith); return str; } // 替换所有指定的字符串 std::string &Str::replaceAll(std::string &str, const std::string &replaceWhat, const std::string &replaceWith) { if (replaceWhat == replaceWith) return str; std::size_t foundAt = std::string::npos; while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) { str.replace(foundAt, replaceWhat.length(), replaceWith); } return str; } // 替换指定的字符串 // 功能介绍:如果目标子串的前一个字符是'%',就仅仅去掉前面的%,然后继续查找,反之,当遇到第一个前面不是‘%’的目标子串时,进行替换,然后替换结束 void Str::replaceFirstWithEscape(base::type::string_t &str, const base::type::string_t &replaceWhat, const base::type::string_t &replaceWith) { std::size_t foundAt = base::type::string_t::npos; while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { str.erase(foundAt - 1, 1); ++foundAt; } else { str.replace(foundAt, replaceWhat.length(), replaceWith); return; } } } #if defined(ELPP_UNICODE) // unicode版本 调用普通版本 void Str::replaceFirstWithEscape(base::type::string_t &str, const base::type::string_t &replaceWhat, const std::string &replaceWith) { replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end())); } #endif // defined(ELPP_UNICODE) // 字符串转为大写 std::string &Str::toUpper(std::string &str) { std::transform(str.begin(), str.end(), str.begin(), [](char c) { return static_cast<char>(::toupper(c)); }); return str; } // 字符串相等比较(区分大小写) bool Str::cStringEq(const char *s1, const char *s2) { if (s1 == nullptr && s2 == nullptr) return true; if (s1 == nullptr || s2 == nullptr) return false; return strcmp(s1, s2) == 0; } // 字符串相等比较(不区分大小写) bool Str::cStringCaseEq(const char *s1, const char *s2) { if (s1 == nullptr && s2 == nullptr) return true; if (s1 == nullptr || s2 == nullptr) return false; // With thanks to cygwin for this code int d = 0; while (true) { const int c1 = toupper(*s1++); const int c2 = toupper(*s2++); if (((d = c1 - c2) != 0) || (c2 == '\0')) { break; } } return d == 0; } // 判断字符串当中是否含有某个字符 bool Str::contains(const char *str, char c) { for (; *str; ++str) { if (*str == c) return true; } return false; } // 就是用于将数字转字符,推荐做法是使用printf函数家族,也不存在性能问题。 char *Str::convertAndAddToBuff(std::size_t n, int len, char *buf, const char *bufLim, bool zeroPadded) { char localBuff[10] = ""; char *p = localBuff + sizeof(localBuff) - 2; if (n > 0) { for (; n > 0 && p > localBuff && len > 0; n /= 10, --len) *--p = static_cast<char>(n % 10 + '0'); } else { *--p = '0'; --len; } if (zeroPadded) while (p > localBuff && len-- > 0) *--p = static_cast<char>('0'); return addToBuff(p, buf, bufLim); } // 将字符串的内容添加到某个缓冲区中 char *Str::addToBuff(const char *str, char *buf, const char *bufLim) { while ((buf < bufLim) && ((*buf = *str++) != '\0')) ++buf; return buf; } // 缓冲区清空 char *Str::clearBuff(char buff[], std::size_t lim) { STRCPY(buff, "", lim); ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro return buff; } // wchar*转为char* /// @brief Converts wchar* to char* /// NOTE: Need to free return value after use! char *Str::wcharPtrToCharPtr(const wchar_t *line) { std::size_t len_ = wcslen(line) + 1; char *buff_ = static_cast<char *>(malloc(len_ + 1)); #if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) std::wcstombs(buff_, line, len_); #elif ELPP_OS_WINDOWS std::size_t convCount_ = 0; mbstate_t mbState_; ::memset(static_cast<void *>(&mbState_), 0, sizeof(mbState_)); wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_); #endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) return buff_; }
DateTime 类
DateTime
类是日期时间工具类/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str class DateTime : base::StaticClass { public: // 获取当前时间(跨平台) /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current microsecond. /// /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a separate implementation is provided /// @param [in,out] tv Pointer that gets updated static void gettimeofday(struct timeval *tv); // 根据指定的时间日期格式将时间转为为对应的字符串形式 /// @brief Gets current date and time with a subsecond part. /// @param format User provided date/time format /// @param ssPrec A pointer to base::SubsecondPrecision from configuration (non-null) /// @returns string based date time in specified format. static std::string getDateTime(const char *format, const base::SubsecondPrecision *ssPrec); // timeval结构体转字符串 /// @brief Converts timeval (struct from ctime) to string using specified format and subsecond precision static std::string timevalToString(struct timeval tval, const char *format, const el::base::SubsecondPrecision *ssPrec); // 将指定的时间戳转化为指定时间单位的字符串形式 /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit); // 求timeval结构体之间的时间差(以毫秒或者微秒为单位) /// @brief Gets time difference in milli/micro second depending on timestampUnit static unsigned long long getTimeDifference(const struct timeval &endTime, const struct timeval &startTime, base::TimestampUnit timestampUnit); // 获取当前时间的tm结构体 static struct ::tm *buildTimeInfo(struct timeval *currTime, struct ::tm *timeInfo); private: // 将日期时间按照指定的格式转为为对应的字符串形式(用于timevalToString) static char *parseFormat(char *buf, std::size_t bufSz, const char *format, const struct tm *tInfo, std::size_t msec, const base::SubsecondPrecision *ssPrec); }; // 获取当前时间(跨平台) void DateTime::gettimeofday(struct timeval *tv) { #if ELPP_OS_WINDOWS if (tv != nullptr) { #if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) const unsigned __int64 delta_ = 11644473600000000Ui64; #else const unsigned __int64 delta_ = 11644473600000000ULL; #endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) const double secOffSet = 0.000001; const unsigned long usecOffSet = 1000000; FILETIME fileTime; GetSystemTimeAsFileTime(&fileTime); unsigned __int64 present = 0; present |= fileTime.dwHighDateTime; present = present << 32; present |= fileTime.dwLowDateTime; present /= 10; // mic-sec // Subtract the difference present -= delta_; tv->tv_sec = static_cast<long>(present * secOffSet); tv->tv_usec = static_cast<long>(present % usecOffSet); } #else ::gettimeofday(tv, nullptr); #endif // ELPP_OS_WINDOWS } // 根据指定的时间日期格式将时间转为为对应的字符串形式 std::string DateTime::getDateTime(const char *format, const base::SubsecondPrecision *ssPrec) { struct timeval currTime; gettimeofday(&currTime); return timevalToString(currTime, format, ssPrec); } // timeval结构体转字符串 std::string DateTime::timevalToString(struct timeval tval, const char *format, const el::base::SubsecondPrecision *ssPrec) { struct ::tm timeInfo; buildTimeInfo(&tval, &timeInfo); const int kBuffSize = 30; char buff_[kBuffSize] = ""; parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast<std::size_t>(tval.tv_usec / ssPrec->m_offset), ssPrec); return std::string(buff_); } const struct { double value; const base::type::char_t *unit; } kTimeFormats[] = { {1000.0f, ELPP_LITERAL("us")}, {1000.0f, ELPP_LITERAL("ms")}, {60.0f, ELPP_LITERAL("seconds")}, {60.0f, ELPP_LITERAL("minutes")}, {24.0f, ELPP_LITERAL("hours")}, {7.0f, ELPP_LITERAL("days")}}; // 将指定的时间戳转化为指定时间单位的字符串形式 base::type::string_t DateTime::formatTime(unsigned long long time, base::TimestampUnit timestampUnit) { base::type::EnumType start = static_cast<base::type::EnumType>(timestampUnit); const base::type::char_t *unit = base::consts::kTimeFormats[start].unit; // 循环的这段逻辑没完全看明白,看注释大概知道是逐步找到最合适的那个时间单位,但和代码还没有完全对应上 /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) { // 小于一个时间单位的值 if (time <= base::consts::kTimeFormats[i].value) { break; } // 以1000为一个基准时间单位("us"或者"ms"),但不明白的是这里为什么要time / 1000.0f < 1.9f用这个作为条件,有清楚的同学可以在留言区说一下,谢谢。 if (base::consts::kTimeFormats[i].value == 1000.0f && time / 1000.0f < 1.9f) { break; } time /= static_cast<decltype(time)>(base::consts::kTimeFormats[i].value); unit = base::consts::kTimeFormats[i + 1].unit; } base::type::stringstream_t ss; ss << time << " " << unit; return ss.str(); } // 求timeval结构体之间的时间差(以毫秒或者微秒为单位) unsigned long long DateTime::getTimeDifference(const struct timeval &endTime, const struct timeval &startTime, base::TimestampUnit timestampUnit) { if (timestampUnit == base::TimestampUnit::Microsecond) { return static_cast<unsigned long long>(static_cast<unsigned long long>(1000000 * endTime.tv_sec + endTime.tv_usec) - static_cast<unsigned long long>(1000000 * startTime.tv_sec + startTime.tv_usec)); } // milliseconds auto conv = [](const struct timeval &tim) { return static_cast<unsigned long long>((tim.tv_sec * 1000) + (tim.tv_usec / 1000)); }; return static_cast<unsigned long long>(conv(endTime) - conv(startTime)); } // 获取当前时间的tm结构体 struct ::tm *DateTime::buildTimeInfo(struct timeval *currTime, struct ::tm *timeInfo) { #if ELPP_OS_UNIX time_t rawTime = currTime->tv_sec; ::elpptime_r(&rawTime, timeInfo); return timeInfo; #else #if ELPP_COMPILER_MSVC ELPP_UNUSED(currTime); time_t t; #if defined(_USE_32BIT_TIME_T) _time32(&t); #else _time64(&t); #endif elpptime_s(timeInfo, &t); return timeInfo; #else // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method time_t rawTime = currTime->tv_sec; struct tm *tmInf = elpptime(&rawTime); *timeInfo = *tmInf; return timeInfo; #endif // ELPP_COMPILER_MSVC #endif // ELPP_OS_UNIX } // 将日期时间按照指定的格式转为为对应的字符串形式(用于timevalToString) char *DateTime::parseFormat(char *buf, std::size_t bufSz, const char *format, const struct tm *tInfo, std::size_t msec, const base::SubsecondPrecision *ssPrec) { const char *bufLim = buf + bufSz; for (; *format; ++format) { if (*format == base::consts::kFormatSpecifierChar) { switch (*++format) { case base::consts::kFormatSpecifierChar: // Escape break; case '\0': // End --format; break; case 'd': // Day buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim); continue; case 'a': // Day of week (short) buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim); continue; case 'A': // Day of week (long) buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim); continue; case 'M': // month buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim); continue; case 'b': // month (short) buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim); continue; case 'B': // month (long) buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim); continue; case 'y': // year (two digits) buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim); continue; case 'Y': // year (four digits) buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim); continue; case 'h': // hour (12-hour) buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim); continue; case 'H': // hour (24-hour) buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim); continue; case 'm': // minute buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim); continue; case 's': // second buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim); continue; case 'z': // subsecond part case 'g': buf = base::utils::Str::convertAndAddToBuff(msec, ssPrec->m_width, buf, bufLim); continue; case 'F': // AM/PM buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim); continue; default: continue; } } if (buf == bufLim) break; *buf++ = *format; } return buf; }
OS 类
OS
类是操作系统相关工具类/// @brief Operating System helper static class used internally. You should not use it. class OS : base::StaticClass { public: #if ELPP_OS_WINDOWS // 获取windows系统环境变量的值 /// @brief Gets environment variables for Windows based OS. /// We are not using <code>getenv(const char*)</code> because of CRT deprecation /// @param varname Variable name to get environment variable value for /// @return If variable exist the value of it otherwise nullptr static const char *getWindowsEnvironmentVariable(const char *varname); #endif // ELPP_OS_WINDOWS // 安卓系统相关 #if ELPP_OS_ANDROID /// @brief Reads android property value static std::string getProperty(const char *prop); /// @brief Reads android device name static std::string getDeviceName(void); #endif // ELPP_OS_ANDROID // 获取控制台命令执行结果(仅支持类unix系统,其他返回空字符串) /// @brief Runs command on terminal and returns the output. /// /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned. /// @param command Bash command /// @return Result of bash output or empty string if no result found. static const std::string getBashOutput(const char *command); // 获取环境变量的值(跨平台) /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++) /// @param variableName Environment variable name /// @param defaultVal If no environment variable or value found the value to return by default /// @param alternativeBashCommand If environment variable not found what would be alternative bash command /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami' static std::string getEnvironmentVariable(const char *variableName, const char *defaultVal, const char *alternativeBashCommand = nullptr); // 获取当前用户名(跨平台) /// @brief Gets current username. static std::string currentUser(void); // 获取当前主机名(跨平台) /// @brief Gets current host name or computer name. /// /// @detail For android systems this is device name with its manufacturer and model separated by hyphen static std::string currentHost(void); // 是否终端支持彩色显示(跨平台) /// @brief Whether or not terminal supports colors static bool termSupportsColor(void); }; #if ELPP_OS_WINDOWS // 获取windows系统环境变量的值 /// @brief Gets environment variables for Windows based OS. /// We are not using <code>getenv(const char*)</code> because of CRT deprecation /// @param varname Variable name to get environment variable value for /// @return If variable exist the value of it otherwise nullptr const char *OS::getWindowsEnvironmentVariable(const char *varname) { const DWORD bufferLen = 50; static char buffer[bufferLen]; if (GetEnvironmentVariableA(varname, buffer, bufferLen)) { return buffer; } return nullptr; } #endif // ELPP_OS_WINDOWS // 安卓系统相关 #if ELPP_OS_ANDROID std::string OS::getProperty(const char *prop) { char propVal[PROP_VALUE_MAX + 1]; int ret = __system_property_get(prop, propVal); return ret == 0 ? std::string() : std::string(propVal); } std::string OS::getDeviceName(void) { std::stringstream ss; std::string manufacturer = getProperty("ro.product.manufacturer"); std::string model = getProperty("ro.product.model"); if (manufacturer.empty() || model.empty()) { return std::string(); } ss << manufacturer << "-" << model; return ss.str(); } #endif // ELPP_OS_ANDROID // 获取控制台命令执行结果(仅支持类unix系统,其他返回空字符串) const std::string OS::getBashOutput(const char *command) { #if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) if (command == nullptr) { return std::string(); } FILE *proc = nullptr; if ((proc = popen(command, "r")) == nullptr) { ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true); return std::string(); } char hBuff[4096]; if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { pclose(proc); const std::size_t buffLen = strlen(hBuff); if (buffLen > 0 && hBuff[buffLen - 1] == '\n') { hBuff[buffLen - 1] = '\0'; } return std::string(hBuff); } else { pclose(proc); } return std::string(); #else ELPP_UNUSED(command); return std::string(); #endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) } // 获取环境变量的值(跨平台) std::string OS::getEnvironmentVariable(const char *variableName, const char *defaultVal, const char *alternativeBashCommand) { #if ELPP_OS_UNIX const char *val = getenv(variableName); #elif ELPP_OS_WINDOWS const char *val = getWindowsEnvironmentVariable(variableName); #endif // ELPP_OS_UNIX if ((val == nullptr) || ((strcmp(val, "") == 0))) { #if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) // Try harder on unix-based systems std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand); if (valBash.empty()) { return std::string(defaultVal); } else { return valBash; } #elif ELPP_OS_WINDOWS || ELPP_OS_UNIX ELPP_UNUSED(alternativeBashCommand); return std::string(defaultVal); #endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) } return std::string(val); } // 获取当前用户名(跨平台) std::string OS::currentUser(void) { #if ELPP_OS_UNIX && !ELPP_OS_ANDROID return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami"); #elif ELPP_OS_WINDOWS return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser); #elif ELPP_OS_ANDROID ELPP_UNUSED(base::consts::kUnknownUser); return std::string("android"); #else return std::string(); #endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID } // 获取当前主机名(跨平台) std::string OS::currentHost(void) { #if ELPP_OS_UNIX && !ELPP_OS_ANDROID return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname"); #elif ELPP_OS_WINDOWS return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost); #elif ELPP_OS_ANDROID ELPP_UNUSED(base::consts::kUnknownHost); return getDeviceName(); #else return std::string(); #endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID } // 是否终端支持彩色显示(跨平台) bool OS::termSupportsColor(void) { std::string term = getEnvironmentVariable("TERM", ""); return term == "xterm" || term == "xterm-color" || term == "xterm-256color" || term == "screen" || term == "linux" || term == "cygwin" || term == "screen-256color"; }
OS
类的相关接口都是调用操作系统底层相关的API
,这里就不多说了。
至此,工具类就差不多分析完了,最后还剩下线程安全相关的类没有介绍。下一篇我们来看看线程安全相关的一些类。
本文来自博客园,作者:节奏自由,转载请注明原文链接:https://www.cnblogs.com/DesignLife/p/16970002.html