Log4cplus 是 log4j 的 C++ 实现,其接口和使用逻辑与 log4j 基本保持一致。
Log4cplus 的每个文件是使用二级BSD许可协议(Two clause BSD license)或者 Apache license 2.0 许可协议,其中的线程池(ThreadPool.h)又是使用另外的协议。
类 | 说明 |
---|---|
Filter | 过滤器,过滤输出消息 |
Layout | 布局器,控制输出消息的格式 |
Appender | 附加器,将日志输出到所附加的设备终端如控制台、调试器、文件、远程服务器等等 |
Logger | 记录器,保存并跟踪对象日志信息变更的实体,当你需要对一个对象进行记录时,就需要生成一个logger |
Hierarchy | 分类器,层次化的树型结构,用于对被记录信息的分类,层次中每一个节点维护一个logger的所有信息 |
LogLevel | 优先权,包括TRACE, DEBUG, INFO, WARNING, ERROR, FATAL |
过滤器,用于过滤日志项,可继承Filter自定义过滤器,也可用自带的过滤器
布局器,控制输出日志消息的格式
1 | new PatternLayout(LOG4CPLUS_TEXT("[%D{%Y-%m-%d %H:%M:%S.%q}] [%t] %-5p [%M] %m%n")); |
1 | [2020-08-24 01:30:43.650] [14168] DEBUG [main] log test |
附加器,将日志输出到所附加的设备终端如控制台、调试器、文件、远程服务器等等
1 | //! Asynchronous append. |
1 | log4cplus::helpers::Properties properties; |
1 | log4cplus::helpers::Properties properties; |
1 | RollingFileAppender(const log4cplus::tstring& filename, |
记录器,保存并跟踪对象日志信息变更的实体,当你需要对一个对象进行记录时,就需要生成一个logger
1 | void log(LogLevel ll, const log4cplus::tstring& message, |
分类器,层次化的树型结构,用于对被记录信息的分类,层次中每一个节点维护一个logger的所有信息
1 | typedef std::vector<Logger> ProvisionNode; |
1 | typedef int LogLevel; |
1 | const LogLevel OFF_LOG_LEVEL = 60000; |
属性,用于配置参数
1 | log4cplus::helpers::Properties properties; |
1 | LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT("Hello world")); |
LOG4CPLUS_INFO_FMT1
2
LOG4CPLUS_MACRO_FMT_BODY (logger, INFO_LOG_LEVEL, __VA_ARGS__)
LOG4CPLUS_MACRO_FMT_BODY1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LOG4CPLUS_SUPPRESS_DOWHILE_WARNING() \
do { \
log4cplus::Logger const & _l \
= log4cplus::detail::macros_get_logger (logger); \
if (LOG4CPLUS_MACRO_LOGLEVEL_PRED ( \
_l.isEnabledFor (log4cplus::logLevel), logLevel)) { \
LOG4CPLUS_MACRO_INSTANTIATE_SNPRINTF_BUF (_snpbuf); \
log4cplus::tchar const * _logEvent \
= _snpbuf.print (__VA_ARGS__); \
log4cplus::detail::macro_forced_log (_l, \
log4cplus::logLevel, _logEvent, \
LOG4CPLUS_MACRO_FILE (), __LINE__, \
LOG4CPLUS_MACRO_FUNCTION ()); \
} \
} while(0) \
LOG4CPLUS_RESTORE_DOWHILE_WARNING()
LOG4CPLUS_MACRO_INSTANTIATE_SNPRINTF_BUF1
2
3
log4cplus::helpers::snprintf_buf & var \
= log4cplus::detail::get_macro_body_snprintf_buf ()
snprintf_buf::print1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19tchar const *
snprintf_buf::print (tchar const * fmt, ...)
{
assert (fmt);
tchar const * str = nullptr;
int ret = 0;
std::va_list args;
do
{
va_start (args, fmt);
ret = print_va_list (str, fmt, args);
va_end (args);
}
while (ret == -1);
return str;
}
snprintf_buf::print_va_list1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43int
snprintf_buf::print_va_list (tchar const * & str, tchar const * fmt,
std::va_list args)
{
int printed;
std::size_t const fmt_len = std::char_traits<tchar>::length (fmt);
std::size_t buf_size = buf.size ();
std::size_t const output_estimate = fmt_len + fmt_len / 2 + 1;
if (output_estimate > buf_size)
buf.resize (buf_size = output_estimate);
//...
printed = vsntprintf (&buf[0], buf_size - 1, fmt, args);
if (printed == -1)
{
if (errno == EILSEQ)
{
LogLog::getLogLog ()->error (
LOG4CPLUS_TEXT ("Character conversion error when printing"));
// Return zero to terminate the outer loop in
// snprintf_buf::print().
return 0;
}
buf_size *= 2;
buf.resize (buf_size);
}
else if (printed >= static_cast<int>(buf_size - 1))
{
buf_size = printed + 2;
buf.resize (buf_size);
printed = -1;
}
else
buf[printed] = 0;
str = &buf[0];
return printed;
}
其实就是使用了C语音的可变参数宏实现参数可变
vadefs.h1
2
3
4
5
6
7
使用了 _vswprintf_p(sprintf) 做格式化
1 | int _vswprintf_p( |
C语言的函数是从右往左压入栈的,比如一下内存分布
1 | void print_args(int count, ...) { |
1 | | 5 | // 高位地址 |
Logger::log1
2
3
4
5
6void
Logger::log (LogLevel ll, const log4cplus::tstring& message, const char* file,
int line, const char* function) const
{
value->log (ll, message, file, line, function ? function : "");
}
LoggerImpl::log:等级1
2
3
4
5
6
7
8
9
10
11void
LoggerImpl::log(LogLevel loglevel,
const log4cplus::tstring& message,
const char* file,
int line,
const char* function)
{
if(isEnabledFor(loglevel)) {
forcedLog(loglevel, message, file, line, function ? function : "");
}
}
LoggerImpl::forcedLog:获取 InternalLoggingEvent1
2
3
4
5
6
7
8
9
10
11
12
13void
LoggerImpl::forcedLog(LogLevel loglevel,
const log4cplus::tstring& message,
const char* file,
int line,
const char* function)
{
spi::InternalLoggingEvent & ev = internal::get_ptd ()->forced_log_ev;
assert (function);
ev.setLoggingEvent (this->getName(), loglevel, message, file, line,
function);
callAppenders(ev);
}
LoggerImpl::callAppenders:遍历父子附加器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22void
LoggerImpl::callAppenders(const InternalLoggingEvent& event)
{
int writes = 0;
for(const LoggerImpl* c = this; c != nullptr; c=c->parent.get()) {
writes += c->appendLoopOnAppenders(event);
if(!c->additive) {
break;
}
}
// No appenders in hierarchy, warn user only once.
if(!hierarchy.emittedNoAppenderWarning && writes == 0) {
helpers::getLogLog().error(
LOG4CPLUS_TEXT("No appenders could be found for logger (")
+ getName()
+ LOG4CPLUS_TEXT(")."));
helpers::getLogLog().error(
LOG4CPLUS_TEXT("Please initialize the log4cplus system properly."));
hierarchy.emittedNoAppenderWarning = true;
}
}
AppenderAttachableImpl::appendLoopOnAppenders:遍历附加器列表1
2
3
4
5
6
7
8
9
10
11
12int
AppenderAttachableImpl::appendLoopOnAppenders(const spi::InternalLoggingEvent& event) const
{
int count = 0;
thread::MutexGuard guard (appender_list_mutex);
for (auto & appender : appenderList)
{
++count;
appender->doAppend(event);
}
return count;
}
Appender::doAppend:同步异步1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23void
Appender::doAppend(const log4cplus::spi::InternalLoggingEvent& event)
{
if (async)
{
event.gatherThreadSpecificData ();
std::atomic_fetch_add_explicit (&in_flight, std::size_t (1),
std::memory_order_relaxed);
try
{
enqueueAsyncDoAppend (SharedAppenderPtr (this), event);
}
catch (...)
{
subtract_in_flight ();
throw;
}
}
else
#endif
syncDoAppend (event);
}
Appender::syncDoAppend:检查阈值、过滤器、锁1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33void
Appender::syncDoAppend(const log4cplus::spi::InternalLoggingEvent& event)
{
thread::MutexGuard guard (access_mutex);
if(closed) {
helpers::getLogLog().error(
LOG4CPLUS_TEXT("Attempted to append to closed appender named [")
+ name
+ LOG4CPLUS_TEXT("]."));
return;
}
// Check appender's threshold logging level.
if (! isAsSevereAsThreshold(event.getLogLevel()))
return;
// Evaluate filters attached to this appender.
if (checkFilter(filter.get(), event) == spi::DENY)
return;
// Lock system wide lock.
helpers::LockFileGuard lfguard;
if (useLockFile && lockFile.get ())
{
try
{
lfguard.attach_and_lock (*lockFile);
}
catch (std::runtime_error const &)
{
return;
}
}
// Finally append given event.
append(event);
}
FileAppenderBase::append:文件打开、锁定(进程同步)、格式化附加、刷新1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22void
FileAppenderBase::append(const spi::InternalLoggingEvent& event)
{
if(!out.good()) {
if(!reopen()) {
getErrorHandler()->error( LOG4CPLUS_TEXT("file is not open: ")
+ filename);
return;
}
// Resets the error handler to make it
// ready to handle a future append error.
else
getErrorHandler()->reset();
}
if (useLockFile)
out.seekp (0, std::ios_base::end);
layout->formatAndAppend(out, event);
if(immediateFlush || useLockFile)
out.flush();
}
SimpleLayout::formatAndAppend:附加1
2
3
4
5
6
7
8
9void
SimpleLayout::formatAndAppend(log4cplus::tostream& output,
const log4cplus::spi::InternalLoggingEvent& event)
{
output << llmCache.toString(event.getLogLevel())
<< LOG4CPLUS_TEXT(" - ")
<< event.getMessage()
<< LOG4CPLUS_TEXT("\n");
}
编译成动态库,带有很多例子项目
https://github.com/huihut/log4cplus/commit/5d7e51ac6a43e1eaa623e5d2272651458edf85c6
./CMakeLists.txt
1 | option(LOG4CPLUS_BUILD_TESTING "Build the test suite." OFF) |
https://github.com/huihut/log4cplus/commit/4e02f06a5549afca1183801a1424eee221a36bb5
./src/CMakeLists.txt
1 | add_compile_definitions (LOG4CPLUS_STATIC) |
1 |
|
1 |
|
https://github.com/huihut/log4cplusplus
log4cplusplus 是 log4cplus 的包装库
1 | enum Log4CPlusPlusLevel |
1 |
|
在 CEF 发行版网站(Chromium Embedded Framework (CEF) Automated Builds)下载对应的 Standard Distribution 版本,本文以 CEF 75.1.14(cef_binary_75.1.8+g84fed5d+chromium-75.0.3770.100_windows32.tar.bz2)为例
打开 cmake-gui,设置源码和生成路径:
D:/code/git/cef-builds/cef_binary_75.1.14+gc81164e+chromium-75.0.3770.100_windows32
D:/code/git/cef-builds/cef_binary_75.1.14+gc81164e+chromium-75.0.3770.100_windows32/build
点击 Configure 生成配置,修改:
CEF_RUNTIME_LIBRARY_FLAG
从 /MT
改为 /MD
USE_SANDBOX
点击 Configure、Generate 生成 VS 工程
ceftests 项目会有文件格式错误1
2
3
4
5
6
7
81>------ 已启动生成: 项目: ceftests, 配置: Debug Win32 ------
1>os_rendering_unittest.cc
1>D:\code\git\cef-builds\cef_binary_75.1.14+gc81164e+chromium-75.0.3770.100_windows32\tests\ceftests\os_rendering_unittest.cc(794): error C2220: 警告被视为错误 - 没有生成“object”文件
1>D:\code\git\cef-builds\cef_binary_75.1.14+gc81164e+chromium-75.0.3770.100_windows32\tests\ceftests\os_rendering_unittest.cc(794): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>D:\code\git\cef-builds\cef_binary_75.1.14+gc81164e+chromium-75.0.3770.100_windows32\tests\ceftests\os_rendering_unittest.cc(955): error C2001: 常量中有换行符
1>D:\code\git\cef-builds\CEF 79.1.38\cef_binary_75.1.14+gc81164e+chromium-75.0.3770.100_windows32\tests\ceftests\os_rendering_unittest.cc(998): error C2001: 常量中有换行符
1>D:\code\git\cef-builds\cef_binary_75.1.14+gc81164e+chromium-75.0.3770.100_windows32\tests\ceftests\os_rendering_unittest.cc(998): fatal error C1075: “{”: 未找到匹配令牌
1>已完成生成项目“ceftests.vcxproj”的操作 - 失败。
用记事本打开 os_rendering_unittest.cc
文件,另存为 带有 BOM 的 UTF-8
编码,覆盖原文件。
重新编译则会编译通过。
然后设置 cefclient
为启动项目,F5,则可看到 Google 为主页的一个浏览器 Demo
Github 地址:LogConsumer
LogConsumer.cs
中的 logFileName
修改为你的路径文件名1 | HuiHut.LogConsumer.LogConsumer.Instance.Write("your log content"); |
命令行
1 | dotnet build |
Visual Studio
LogConsumer.sln
10 个线程,每个线程抛出 10 条日志的测试
1 | Data Time Namespace Class Method LogContent |
能获得的信息如下(系统、硬件、网络信息已打码)
1 | --------- 系统信息 --------- |
硬件网络信息是通过 System.Management 里面的类方法获取的,具体类方法可查看官方文档:
软件信息是通过读取注册表的方式获取的,所以软件开发使用的话需要验证一下某些安全管家是否会警告。
1 | 计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall\ |
测试了一下读取注册表,360,腾讯管家不会警告,但有些软件(如:procmon)能监控进程的读取的行为。
]]>使用 VS 在 Unity 编辑器中调试代码,点击 VS 的 附加到 Unity
,Unity 编辑器按下 Play
之后,就会一直等待并且无其他响应,只能结束 Unity 进程。
经过分析与测试,发现去掉代码中一个单例类的析构方法中的断点即可调试了,具体原因未知。
再测试了一下,在那个单例类的构造、析构方法中分别打断点,如下表:
测试 | 构造 | 析构 | 结果 |
---|---|---|---|
1 | 可调试 | ||
2 | * | 可调试 | |
3 | * | 无响应 | |
4 | * | * | 无响应 |
表中
*
代表打断点
可发现只要在那个单例类的析构中打断点就会造成 Unity 无响应。
删除析构方法中的断点,或者所有断点,再调试。
]]>1 | E:\PyCharm 2018.2.5\helpers\pycharm\docrunner.py:1: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses |
imp 从 Python 3.4 之后弃用了,建议使用 importlib 代替
打开 E:\PyCharm 2018.2.5\helpers\pycharm\docrunner.py
文件,做如下两步修改:
在第一行,注释掉 imp
,导入 importlib
1 | #import imp |
在第 230 行的 loadSource
函数中,注释 imp.load_source
,使用 importlib.machinery.SourceFileLoader
加载模块
1 | #module = imp.load_source(moduleName, fileName) |
至今为止,关于运行失败的 issue 作者也没有回复,希望有朝一日能填坑吧~
1 | git clone --recursive git@github.com:patrikhuber/eos.git |
打开 CMake-gui,设置
D:/code/VS/BabyCreator/eos
D:/code/VS/BabyCreator/eos_build
点击 Configure,设置编译器
配置过程遇到如下错误。
1 | Found OpenCV Windows Pack but it has no binaries compatible with your |
能找到 OpenCV 路径,但找不到兼容的二进制文件。
出错的这个 OpenCV 是在 opencv.org/releases 下载的 Win pack。
尝试过网友的解决方案【尝试二、三、四】都不能解决,最终通过【尝试五】重新下载源码编译解决,因此可能是构建环境或者编译器的问题。
【尝试一】重新设置环境变量:E:\opencv-4.0.1\opencv-4.0.1-winpack\opencv\build\x64\vc15\bin
【尝试二】在 eos/CMakeCache.txt
设置 OpenCV_DIR,不行。
1 | set(OpenCV_DIR "D:\\code\\VS\\BabyCreator\\eos\\3rdparty\\opencv\\build") |
【尝试三】在终端设置 OpenCV_DIR,不行。
1 | PS D:\code\VS\BabyCreator\eos> cmake . -DOpenCV_DIR=D:\\code\\VS\\BabyCreator\\eos\\3rdparty\\opencv\\build\\OpenCVConfig.cmake |
【尝试四】在 find_package 前设置 OpenCV_FOUND 为 1,不行。
1 | set(OpenCV_FOUND 1) |
【尝试五】下载源码编译 VS 2017 工程的 opencv,解决。
install/setup_vars_opencv4.cmd
E:/opencv-4.0.1/opencv-4.0.1-vs-build/install/x86/vc15/bin
1 | CMake Warning at E:/CMake/share/cmake-3.12/Modules/FindBoost.cmake:577 (message): |
没找到 Boost 库,原因是没配置好。
尝试了如下方式后,这个问题变成了 Unable to find the requested Boost libraries.
boost_1_66_0/b2.exe
生成静态库eos/CMakeCache.txt
配置 1 | set(BOOST_ROOT "E:\\boost_1_66_0") |
1 | CMake Error at E:/CMake/share/cmake-3.12/Modules/FindBoost.cmake:2048 (message): |
找到了 Boost 库,但是找不到 boost_system
、boost_filesystem
、boost_program_options
这三个库。
这个问题太坑了,最终是把 E:\boost_1_66_0\stage\lib
里面的那三个库(每个有四个相关库)的名字分别删掉 lib
,如下对 boost_system
的修改:
libboost_system-vc141-mt-x32-1_66.lib
改为 boost_system-vc141-mt-x32-1_66.lib
libboost_system-vc141-mt-gd-x32-1_66.lib
改为 boost_system-vc141-mt-gd-x32-1_66.lib
libboost_system-vc141-mt-x64-1_66.lib
改为 boost_system-vc141-mt-x64-1_66.lib
libboost_system-vc141-mt-gd-x64-1_66.lib
改为 boost_system-vc141-mt-gd-x64-1_66.lib
现在终于可以生成工程文件了!
然而 VS 打开工程后编译,又有问题。
generate-obj.vcxproj1
eos\include\eos\render\texture.hpp(101): error C2065: “CV_BGR2BGRA”: 未声明的标识符 (undeclared identifier)
这个问题可以根据这个 issue,把 CV_BGR2BGRA
改为 cv::COLOR_BGR2BGRA
,或者改为 0
解决。
fit-model-simple.vcxproj1
2
3
4
5
6
7
81>boost_program_options-vc140-mt-gd.lib(boost_program_options-vc141-mt-gd-x32-1_68.dll) : error LNK2005: "public: __thiscall boost::program_options::value_semantic_codecvt_helper<char>::value_semantic_codecvt_helper<char>(void)" (??0?$value_semantic_codecvt_helper@D@program_options@boost@@QAE@XZ) 已经在 boost_program_options-vc141-mt-gd-x32-1_66.lib(value_semantic.obj) 中定义
1>boost_program_options-vc140-mt-gd.lib(boost_program_options-vc141-mt-gd-x32-1_68.dll) : error LNK2005: "public: virtual __thiscall boost::program_options::value_semantic_codecvt_helper<char>::~value_semantic_codecvt_helper<char>(void)" (??1?$value_semantic_codecvt_helper@D@program_options@boost@@UAE@XZ) 已经在 boost_program_options-vc141-mt-gd-x32-1_66.lib(value_semantic.obj) 中定义
1>boost_program_options-vc140-mt-gd.lib(boost_program_options-vc141-mt-gd-x32-1_68.dll) : error LNK2005: "void __cdecl boost::program_options::validate(class boost::any &,class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *,int)" (?validate@program_options@boost@@YAXAAVany@2@ABV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@std@@PAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@H@Z) 已经在 boost_program_options-vc141-mt-gd-x32-1_66.lib(value_semantic.obj) 中定义
1>boost_program_options-vc140-mt-gd.lib(boost_program_options-vc141-mt-gd-x32-1_68.dll) : error LNK2005: "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl boost::program_options::to_internal(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?to_internal@program_options@boost@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@ABV34@@Z) 已经在 boost_program_options-vc141-mt-gd-x32-1_66.lib(convert.obj) 中定义
1> 正在创建库 D:/code/VS/BabyCreator/eos_build/examples/Debug/fit-model-simple.lib 和对象 D:/code/VS/BabyCreator/eos_build/examples/Debug/fit-model-simple.exp
1>fit-model-simple.obj : error LNK2019: 无法解析的外部符号 "class cv::Mat __cdecl cv::imread(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,int)" (?imread@cv@@YA?AVMat@1@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z),该符号在函数 __catch$_main$0 中被引用
1>fit-model-simple.obj : error LNK2019: 无法解析的外部符号 "bool __cdecl cv::imwrite(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class cv::debug_build_guard::_InputArray const &,class std::vector<int,class std::allocator<int> > const &)" (?imwrite@cv@@YA_NABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@ABV_InputArray@debug_build_guard@1@ABV?$vector@HV?$allocator@H@std@@@3@@Z),该符号在函数 __catch$_main$6 中被引用
1>D:\code\VS\BabyCreator\eos_build\examples\Debug\fit-model-simple.exe : fatal error LNK1120: 2 个无法解析的外部命令
这个问题可能是安装的这个版本的 boost 库与什么东西不兼容吧,又是环境问题,我已经无力吐槽 Windows 了(心累)。
这个问题我在官方仓库提了个 issue。
作者的意思是:我没试过你的 boost 和 opencv 的版本,可能是本地配置问题,建议用构建系统(如 vcpkg) 或者在 StackOverflow 上提问。
我:emmm……
所以,这个问题未解决,最后是用 vcpkg 安装库解决的。
安装 vcpkg、opencv、boost1
2
3
4git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg install opencv boost
安装完成后,在 eos
同级目录下创建 build 文件夹 eos_vcpkg_build
,如下目录结构:
1 | - eos |
然后执行下面的命令构建工程:
1 | mkdir eos_vcpkg_build |
OpenCV_DIR
设为 vcpkg 安装的 opencv 路径
BOOST_ROOT
设为 vcpkg 安装的 boost 路径
BOOST_LIBRARYDIR
设为 vcpkg 安装的 boost 库文件路径
BOOST_INCLUDEDIR
设为 vcpkg 安装的 boost 头文件路径
CMAKE_INSTALL_PREFIX
即为 make install 的路径
构建好后,用 VS 打开,右键 解决方案 eos
- 生成解决方案
然后选择 INSTALL
工程 - 右键 设为启动项目
- 右键 生成
居然成功编译并安装好了(喜极而泣)!
运行下 eos_vcpkg_build\install\bin\fit-model.exe
示例程序吧 ~
根据墨菲定律,必定没这么顺利,果然……
一连跳几个框,
1 | 由于找不到 opencv_core343.dll、opencv_imgcodecs343.dll、boost_filesystem-vc141-mt-x32-1_68.dll、jpeg62.dll、zlib1.dll 等,无法继续执行代码。重新安装重新可能会解决此问题。 |
既然缺少动态库,那就去 vcpkg 安装路径下找,如果没有则用 vcpkg 安装,然后把这些 dll 放到 fit-model.exe
同级目录下。
然后就……
1 | 应用程序无法正常启动(0xc000007b)。请点击“确定”关闭应用程序。 |
我:emmm……mmp
应该是动态库的链接错误,关于这个问题我再次开了 issue,但是作者不再回应了。
所以,在 Windows 上,只是编译成功库,但是示例都无法运行。
最终,在 Ubuntu 上,编译运行起来了。
心累。
]]>1 | Plugin 'opencvforunity.dll' is used from several locations: |
我使用的 opencvforunity.dll
库的 32 位版本与 64 位版本在 Unity 编辑器中没有明确指定,所以 Unity 不知道 32 位或者 64 位系统下用哪个 opencvforunity.dll
库。
opencvforunity.dll
取消 x86_64 平台,只勾选 x86 平台,Applyopencvforunity.dll
取消 x86 平台,只勾选 x86_64 平台,Apply本文是 huihut . C/C++面试基础知识总结 仓库的一部分,定位链接:Effective C++
const
、enum
、inline
替换 #define
)operator=
返回一个 reference to *this
(用于连锁赋值)operator=
中处理 “自我赋值”new
中使用 []
则 delete []
,new
中不使用 []
则 delete
)(T)expression
、T(expression)
;新式:const_cast<T>(expression)
、dynamic_cast<T>(expression)
、reinterpret_cast<T>(expression)
、static_cast<T>(expression)
、;尽量避免转型、注重效率避免 dynamic_casts、尽量设计成无需转型、可把转型封装成函数、宁可用新式转型)tr1::function
成员变量替换 virtual 函数,将继承体系内的 virtual 函数替换为另一个继承体系内的 virtual 函数)this->
指涉 base class templates 内的成员名称,或藉由一个明白写出的 “base class 资格修饰符” 完成)七牛云对测试域名进行回收,而我博文中图片用的链接仍然是测试域名的链接,因此导致部分链接失效,浏览器返回如下错误:
1 | {"error":"no such domain"} |
经过提交工单与七牛云工程师协商,知道了失效的图片并没有丢失,可通过某些方法找回,解决办法如下:
下载单个图片:命令行辅助工具(qrsctl)(使用 qrsctl 工具的 get 下载),如下:
1 | .\qrsctl.exe login huihut@outlook.com 123 # 假设密码为 123 |
下载多个对象
qshell listbucket <原bucket名> list.txt
(list 出全部文件)cat list.txt | awk '{print $1}' >list_final.txt
( 用awk获取list结果的第一列)qshell batchcopy <原bucket名> <新bucket名> list_final.txt
(复制到新bucket的文件和原bucket文件名一致)qshell qdownload newfilelist.txt
(newfilelist.txt 为下载的配置文档)上面的解决方法中,要想继续使用七牛云存储作为图床,必须要有个实名认证的域名(解决方法一),然后绑定之
而我并没有,因此我打算把我的图片对象迁移到 阿里云 OSS 存储
上们的解决方法二、三都可以,但是二非常麻烦,三需要两个存储空间,也就需要对七牛账号进行实名认证
而我又不想另外实名认证(瞎折腾),因此我用了第二种方法:下载单个图片![捂脸]
使用 qrsctl 工具只能单个文件下载,为了避免重复的工作,我写了个 C++ 程序解决。
首先进入七牛云要下载的 bucket(存储空间)的 web 界面,把内容管理列表中全部对象显示出来(点底部加载更多直到全部显示),在 Chrome 浏览器 右键
- 检查
,如下图,选中 tbody
- 右键
- Copy
- Copy element
然后把复制到的内容保存成 html.html
文件,放到一个目录下,如我的 D:\code\test\
(以下均以此目录为工程目录)
下载 命令行辅助工具(qrsctl),保存到 D:\code\test\
目录,并重命名为 qrsctl.exe
在 D:\code\test\
目录的 CMD 或 PowerShell 中输入如下命令登录你的七牛账号
1 | .\qrsctl.exe login huihut@outlook.com 123 # 假设密码为 123 |
在 D:\code\test\
下创建个 QrsctlGet.cpp
,把下面 C++ 代码保存到刚创建的文件,修改存储空间名为你的存储空间名的名字(本文为 temp
),用 VS Code
或其他工具编译运行,七牛存储的文件即会下载到同目录下。
Github 仓库:huihut/QrsctlGet(有 QrsctlGet.Cpp
、qrsctl.exe
、.vscode环境
)
1 | //============================================================== |
在 阿里云 OSS 存储 创建阿里云账号,并在 OSS 中新建 Bucket,其中读写权限选中公共读(public-read),以便能在博文中读取
把上面下载的图片上传到阿里云 OSS,可以使用客户端上传下载:aliyun/oss-browser
然后获取外链,批量修改我的博文,把所有七牛云链接改为阿里云就好了
]]>C# 程序解压 7z 文件的时候抛出异常
1 | Can not load 7-zip library or internal COM error! Message: DLL file does not exist. |
程序无法找到 7z 库,导致无法解压 7z 文件
选择 Visual Studio 某个项目 - 右键引用 - 管理 NuGet 程序包 - 浏览 - 搜索 7z
,选择一个 7z
包,如 7z.Libs
安装即可。
在官网 Download 7-Zip 下载下面的 7z 库
Link | Type | Windows | Description |
---|---|---|---|
Download | .7z | x86 / x64 | 7-Zip Extra: standalone console version, 7z DLL, Plugin for Far Manager |
解压重命名到 C:\Program Files\7-Zip
1 | /// <summary> |
本博文记录了组装台式机装黑苹果 High Sierra 10.13.6 的经历。
原本想装 Mojave 10.14 的,可惜发现在 Mojave 下还没有 GTX1080 的驱动,所以只能退而求其次装 High Sierra 了。
装 High Sierra 的过程中,第一次使用 10.13.6(17G2112) 镜像遇到个问题(下文有描述),无法进入安装界面,因此后来使用 10.13.5(17F77) 镜像装好后在 AppStore 更新 10.13.6
本博文使用的一些软件工具驱动,下文则不赘述。
准备一个8G以上的U盘,使用 Etcher 刻录上面下载的镜像(带有 Clover EFI 分区):macOS High Sierra 10.13.6(17G2112) Installer with Clover 4606.dmg
BIOS 版本已更新为 F7
保存 BIOS 配置
重启进入刻录好的U盘,选择 Boot macOS Install from Install macOS High Sierra
唠叨模式滚代码的时候出错:please go to https://panic.apple.com to report this panic
上面的问题 Google 了一圈并未发现解决办法,因此重新刻录 macOS High Sierra 10.13.5(17F77) Installer with Clover 4512.dmg
,先装 10.13.5(17F77) 。
这次的 10.13.5(17F77) 成功进入安装界面了。
打开磁盘工具,格好一个系统盘,格式为 APFS 或者 Mac OS 扩展(日志式),大小因人而异,我留了 128G。
格好后安装系统到这个盘。
安装过程中第一次自动重启依然进入 U 盘 Clover,引导进入格出来的盘继续安装。
第二次自动重启也是进入 U 盘 Clover,引导进入格出来的盘,然后安装完毕进入系统。
进入系统后发现 1080 独显不能驱动、八代 CPU 不能识别,这个先不管,先装 Clover 到磁盘的 EFI 分区。
安装 Clover 有两种:
由于 U 盘的 Clover 是已经试验过能引导的,所以我就用了 U 盘的。
在终端上执行 diskutil list
,找到两个 EFI 分区(磁盘的 EFI 和 U 盘的 EFI),如下的 disk0 s1
和 disk3 s1
1 | HuiMac:~ huihut$ diskutil list |
分别挂载到不同名字的卷
挂载磁盘 EFI1
2sudo mkdir /Volumes/efidisk
sudo mount -t msdos /dev/disk0s1 /Volumes/efidisk
挂载 U 盘 EFI1
2sudo mkdir /Volumes/efiusb
sudo mount -t msdos /dev/disk3s1 /Volumes/efiusb
然后把 U 盘的 EFI/CLOVER
这个文件夹复制到 磁盘的 EFI
下
使用 MultiBeast 安装驱动,使用 Nvidia WebDriver 安装显卡驱动(MultiBeast 的 WebDriver 不能驱动我的 1080)。
装好 Nvidia WebDriver 后 1080 能驱动,不过 i7-8700K 的八代 CPU 还是不能识别,先不管,在 AppStore 更新 10.13.6。
更新完 10.13.6 发现许多驱动有问题:
使用适用于 10.13.6 的 WebDriver-387.10.10.10.40.105.pkg,装好重启后就 OK 了
挂载磁盘 EFI 分区(操作如上),使用 Clover Configurator 打开 /EFI/CLOVER/config.plist
在 CPU 页面的 Type 中填入 Unknown,保存。
使用文本编辑器(如 BBEdit)打开 /System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/Resources/zh_CN.lproj/AppleSystemInfo.strings
文件
修改 UnknownCPUKind
的值为 3.7 GHz Intel Core i7-8700K
保存重启即可。
我使用 MultiBeast 安装的 ALC1220 驱动在 10.13.6 下不能驱动我的声卡,试过 MultiBeast 的其他驱动也不行,因此使用另一种方法修复。
在 acidanthera/AppleALC/releases 下载最新的 AppleALC 的 RELEASE 版 AppleALC.kext
,使用 Kext Wizard 注入这个内核,如下图
然后挂载磁盘 EFI 分区(操作如上),把 AppleALC.kext
放入磁盘 EFI 分区的 EFI/CLOVER/kexts/Other/
目录中。
然后在 acidanthera/AppleALC/Resources 找到你的声卡型号的文件夹,进入(如我的是 ALC1220
)。
我的声卡型号看到有 layout1.xml
、layout2.xml
、layout5.xml
、layout7.xml
、layout11.xml
、layout13.xml
然后在 1、2、5、7、11、13 中随便取一个数。
使用文本编辑器打开磁盘 EFI 分区的 EFI/CLOVER/config.plist
文件,搜索 <key>Audio</key>
,把它的 integer
改为刚刚取的那个数(如 1
)。
保存重启即可。
使用上文描述的操作挂载磁盘的 EFI 分区
使用 Clover Configurator 打开 /EFI/CLOVER/config.plist
如下图,插入(若已存在则修改为)配置信息
保存重启即可。
Clover主题可以到官方仓库下载:
也可以使用其他的,比如我使用这个简洁风格的:
只需下载下来,放到 /EFI/CLOVER/themes
文件夹下,然后使用 Clover Configurator 打开 /EFI/CLOVER/config.plist
更换到这个主题就好啦。
另外这个显示启动盘的标签的 Label
建议勾上,这样才知道选择的是哪个盘,保存重启即可。
1 | sudo pip install matplotlib |
安装 matplotlib 时出现以下错误
1 | Found existing installation: pyparsing 1.5.6 |
安装 matplotlib 需要卸载我已安装的 1.5.6 版本的 pyparsing,再重新装新版本,但是无法卸载
手动重装最新版 pyparsing
首先,去官网查看最新版是什么版本:https://pypi.org/project/pyparsing/
当前最新版是 pyparsing 2.2.0
,所以执行如下重新安装最新版:
1 | sudo pip install -I pyparsing==2.2.0 |
1 | sudo pip install matplotlib |
安装 matplotlib 时出现以下错误
1 | Installing collected packages: subprocess32, cycler, backports.functools-lru-cache, pyparsing, kiwisolver, matplotlib |
无法找到 python 库,#include "Python.h"
出错
使用 apt (Ubuntu, Debian…) 安装
1 | sudo apt-get install python-dev # for python2.x installs |
使用 yum (CentOS, RHEL…) 安装
1 | sudo yum install python-devel # for python2.x installs |
使用 dnf (Fedora…) 安装
1 | sudo dnf install python2-devel # for python2.x installs |
使用 zypper (openSUSE…) 安装
1 | sudo zypper in python-devel # for python2.x installs |
使用 apk (Alpine…) 安装
1 | sudo apk add python2-dev # for python2.x installs |
1 |
|
1 |
|
之前写过的一篇博文:OpenCV使用CMake和MinGW的编译安装及其在Qt配置运行 是使用 32 位的 MinGW 在 Windows 下编译 OpenCV 生成 32 位的 dll。
而这篇博文是使用 64 位的 MinGW 编译 OpenCV 生成 64 位的 dll。
因为博主没有 64 位 qmake,所以没勾选 WITH_QT
Github . huihut/OpenCV-MinGW-Build
下载安装:MinGW-x64-4.8.1-release-posix-seh-rev5
(博文使用 MinGW-x64-4.8.1 为例,但建议使用最新 MinGW:MinGW-w64 - for 32 and 64 bit Windows)
为用户变量 Path
添加 E:\MinGW-w64\x64-4.8.1-release-posix-seh-rev5\mingw64\bin
打开命令提示符 CMD,运行 set PATH=C:
,更改当前窗口任务的环境变量,关闭这个 CMD。
再次打开另一个 CMD,运行 echo %PATH%
,显示最新的环境变量,会发现刚刚添加的 MinGW 环境变量已经生效。
打开 cmake-gui,设置源码和生成路径:
E:/opencv_341/opencv/sources
E:/opencv_341/opencv_mingw64_build
点击 Configure,设置编译器
MinGW Makefiles
E:\MinGW-w64\x64-4.8.1-release-posix-seh-rev5\mingw64\bin\gcc.exe
E:\MinGW-w64\x64-4.8.1-release-posix-seh-rev5\mingw64\bin\g++.exe
编译配置:
WITH_OPENGL
ENABLE_CXX11
WITH_IPP
ENABLE_PRECOMPILED_HEADERS
点击 Configure,Generate 生成 Makefile
博主的配置信息如下:
1 | General configuration for OpenCV 3.4.1 ===================================== |
打开终端进行编译:(-j
是使用 8
个线程进行编译,请根据你的计算机配置合理设置线程数)
1 | E: |
如果 mingw32-make -j 8
遇到错误,请看下面的 编译 OpenCV 常见错误,否则执行 mingw32-make install
,完成安装。
1 | [ 49%] Building CXX object modules/videoio/CMakeFiles/opencv_videoio.dir/src/cap_dshow.cpp.obj |
打开E:\MinGW-w64\x64-4.8.1-release-posix-seh-rev5\mingw64\x86_64-w64-mingw32\include\aviriff.h
发现第一行的多行注释少了个/
符号,加上保存,如下:
1 | /** |
然后重新 Configure
-Generate
-mingw32-make
就好了。
1 | ...... |
因为 MinGW 不能编译 OpenCV 的 cap_msmf 那部分代码,具体见我提的 Issue:Failed to compile opencv-4.0.0-alpha using mingw-w64。
所以,在 cmake-gui 编译配置中:
WITH_MSMF
然后重新 Configure
-Generate
-mingw32-make
1 | [ 86%] Building CXX object modules/calib3d/CMakeFiles/opencv_calib3d.dir/src/chessboard.cpp.obj |
因为 OpenCV 在 chessboard.cpp
、chessboard.hpp
、test_chesscorners.cpp
这些代码中有 BUG,使用的应该是 CV_PI
而不是 M_PI
导致的。具体见我提的 Issue:Failed to compile opencv-4.0.0-alpha using mingw-w64
在最新的 master 分支已经解决了这个问题,见我的 PR :M_PI changed to CV_PI
如果你是在 官网 或者 github.com/opencv/opencv/releases 中下的 OpenCV 4.0.0-alpha
,可能还会有这个问题,那么你需要把 chessboard.cpp
、chessboard.hpp
、test_chesscorners.cpp
文件中的 M_PI
全部改为 CV_PI
,如我的 commit 所示:M_PI changed to CV_PI (#12645)
然后重新 Configure
-Generate
-mingw32-make
1 | [ 28%] Building CXX object modules/CMakeFiles/ade.dir/__/3rdparty/ade/ade-0.1.1c/sources/ade/source/alloc.cpp.obj |
因为我使用了 MinGW-w64-8.1.0
编译,而新的编译器在 Windows 下不再定义 WIN32
,而定义成 _WIN32
,如这个 Issue 的问题:error: ‘posix_memalign’ was not declared in this scope #12831
把 opencv-4.0.0-rc-build\3rdparty\ade\ade-0.1.1c\sources\ade\source\alloc.cpp
文件的所有 WIN32
改为 _WIN32
,如这个 PR 所做的修改:fix check for win32 #6
然后重新 Configure
-Generate
-mingw32-make
1 | [ 32%] Building CXX object modules/core/CMakeFiles/opencv_core.dir/src/directx.cpp.obj |
D3D11_TEXTURE2D_DESC
在 d3d11.h
中定义,貌似在我的机器上找不到定义。反正也不用 D3D11 与 OpenCL 交互,因此可以关闭 WITH_OPENCL_D3D11_NV
这个选项(默认是开的)。可见我的 issue:Error compiling 4.1.0 source code with mingw-w64: ‘D3D11_TEXTURE2D_DESC’ was not declared in this scope #14286
方法一:
在 cmake-gui 编译配置中:
WITH_OPENCL_D3D11_NV
然后重新 Configure
-Generate
-mingw32-make
方法二:
如这个 PR 中对这两个 cmake 文件的修改:cmake: fix WITH_OPENCL_D3D11_NV check #14294,即可先判断是否可以用,再设置默认的 WITH_OPENCL_D3D11_NV
如果以上错误不是你所遇到的,请务必先看下面两个文章中的错误。
创建一个名为 MySharedLib 的共享库
CMakeLists.txt1
2
3
4
5
6cmake_minimum_required(VERSION 3.10)
project(MySharedLib)
set(CMAKE_CXX_STANDARD 11)
add_library(MySharedLib SHARED library.cpp library.h)
library.h1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 打印 Hello World!
void hello();
// 使用可变模版参数求和
template <typename T>
T sum(T t)
{
return t;
}
template <typename T, typename ...Types>
T sum(T first, Types ... rest)
{
return first + sum<T>(rest...);
}
library.cpp1
2
3
4
5
6
void hello() {
std::cout << "Hello, World!" << std::endl;
}
创建一个名为 TestSharedLib 的可执行项目
CMakeLists.txt1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19cmake_minimum_required(VERSION 3.10)
project(TestSharedLib)
# C++11 编译
set(CMAKE_CXX_STANDARD 11)
# 头文件路径
set(INC_DIR /home/xx/code/clion/MySharedLib)
# 库文件路径
set(LIB_DIR /home/xx/code/clion/MySharedLib/cmake-build-debug)
include_directories(${INC_DIR})
link_directories(${LIB_DIR})
link_libraries(MySharedLib)
add_executable(TestSharedLib main.cpp)
# 链接 MySharedLib 库
target_link_libraries(TestSharedLib MySharedLib)
main.cpp1
2
3
4
5
6
7
8
9
10
11
12
13
using std::cout;
using std::endl;
int main() {
hello();
cout << "1 + 2 = " << sum(1,2) << endl;
cout << "1 + 2 + 3 = " << sum(1,2,3) << endl;
return 0;
}
执行结果1
2
3Hello, World!
1 + 2 = 3
1 + 2 + 3 = 6
本文通过 UWP 动画,用两种方法实现这个效果,用于改变周贡献榜和粉丝榜的 Grid 的高度。
1 | <UserControl.Resources> |
1 | // 鼠标悬停周贡、粉丝榜的 Grid |
1 | <Grid x:Name="WeekFansGrid" Background="White" VerticalAlignment="Top" Height="140" |
1 | // 鼠标悬停周贡、粉丝榜的 Grid |
1 | #!/usr/bin/python |