对于普通对象或者函数而言,声明和实现可以分离到*.h和*.cpp(比如说这里写做CommonClass.h和CommomClass.cpp)中去,其中CommonClass.cpp会includeCommonClass.h,因为编译器会根据CommonClass.cpp生成对应CommonClass.obj,由于编译器可以预期类的行为,因此obj文件中会包含实现体对应的二进制代码。如果现有main.cpp使用了该类生成的对象,那么链接器可以在CommonClass.obj找到实现体对应的二进制代码。可是对于模板来说,并不是这样,先看下面这段话。
《C++编程思想》第15章(第300页)说明了原因:
模板定义很特殊。由template<…> 处理的任何东西都意味着编译器在当时不为它分配存储空间,它一直处于等待状态直到被一个模板实例告知。在编译器和连接器的某一处,有一机制能去掉指定模板的多重定义。所以为了容易使用,几乎总是在头文件中放置全部的模板声明和定义。
上面这段话的意思就是说,只有模板实例化时,编译器才会得知T实参是什么。编译器在处理模板实例化时,不仅仅要看到模板的定义式,还需要模版的实现体。
比如说存在模板CTest, 其类定义式写在CTest.h,类的实现体写在CTest.cpp中。对于模板来说,编译器在处理CTest.cpp文件时,编译器无法预知T的实参是什么,所以编译器对其实现是不作处理的(即CTest.obj中没有为编译器为实现体生成的对应的二进制代码)。
现在有main.cpp真正使用了该模板(比方说,生成模板类的一个对象,并调用其函数),如果定义和实现分离,则编译器可以根据定义式生成模板类的对象(因为此处仅仅需要定义式就知道该对象在内存中需要多少空间并进一步分配了),但是调用对象的函数(即真正使用)需要该函数的定义,由于main.cpp仅仅include了模板的声明(所以只能找到该函数的声明),所以无法找到该函数的定义,此时编译器会寄希望于链接器在其他obj文件(这里就是指CTest.obj文件)中寻找该模板的实现体,但是就像之前说过的,CTest.obj中也没有实现体生成的二进制代码。如果定义和实现是在同一个文件(比如说CTest.h)中,那么编译器在编译时就可以寻找到模板的实现体。这里看下面的三个例子。
例1(定义与实现分离,错误示范)
// CTest.
代码
https://github.com/2997215859/HuaWeiCraft2018
版本说明
- v1.0.0 是初赛最后的提交版本, 里面包含了开赛以来截至到初赛结束编写的所有方法的代码
- v1.1.0 是预测只保留了AR模型、svm模型, 分配只保留了背包、分组遗传的纯净版
- master 现在是复赛最终的代码。只有v1.0.0和v1.1.0是初赛的代码
复赛
复赛
凉的透彻,复赛成绩在中游。最高的分数是216。预测算法是二次指数平滑 + 最小二乘 + 中高级乘以系数。然而还是干不过用平均数的的大佬。。各种辛苦撸的模型输给了平均数,,人生污点。。。85分扛把子就此陨落。。。
这里吐槽一些东西。首先,数据量太少,变量只有一个,很扯犊子,而且用例可能没换???然后,评分公式中预测影响太大,而分配影响很小。希望以后比赛出题的时候能再三斟酌,不然很容易毁了口碑(似乎本来也没什么口碑0.0)。同校有一个队,去噪+平均数+乘系数能上238。emmmmm....怀疑人生......不知道头发很多的那个小哥哥作何感想。另外,机器学习不给用库,就是在耍流氓好吧。
复赛前夜
复赛到这个程度,基本是凉凉了,大佬们太凶残,没有什么盼头了,准备去南研所混吃混喝了。
准备了三版代码。明天提交完这场比赛就game over了。
唯一比较遗憾的是初赛的时候没有秀一把。遗憾。
初赛
244,江山赛区。还有上升空间,但是没有机会尝试了。
预测太玄学,加上,上传出现了答案解压失败的情况(后来才知道不占次数),很刺激。
初赛第24名。64不亏,36血赚。
40天写了4万行代码(鬼知道我写了什么东西),日均一千多行。
预测算法:滑动平均 + SVR
分配算法:背包 + 分组遗传
目的
当工作的主项目要用到其他仓库的项目时,就是git 子模块(git submodule)的用武之地了。因为重新下载其他仓库源码并复制到主项目中去,比较麻烦,而且这种工作流显得很怪异。此时使用git submodule是更好的选择。
主项目中添加子模块(子项目)
切换到所要添加子模块的目录中去,输入命令git submodule add git@github.com:2997215859/gr-roi.git
,即会添加该子模块。
ruiy@ruiy-All-Series:~/store/ROI/GModule$ git submodule add git@github.com:2997215859/gr-roi.git
正克隆到 'GModule/gr-roi'...
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (5/5), done.
接收对象中: 100% (5/5), 4.73 KiB | 0 bytes/s, 完成.
remote: Total 5 (delta 0), reused 0 (delta 0), pack-reused 0
检查连接... 完成。
ruiy@ruiy-All-Series:~/store/ROI/GModule$ git status
要提交的变更:
(使用 "git reset HEAD <文件>..." 以取消暂存)
新文件: ../.gitmodules
新文件: gr-roi
此时主项目根目录下也出现了新文件.gitmodule。里面保存着本地子模块的路径以及子模块的上游源
ruiy@ruiy-All-Series:~/store/ROI$ cat .gitmodules
[submodule "GModule/gr-roi"]
path = GModule/gr-roi
url = git@github.com:2997215859/gr-roi.git
切到子模块的上级目录,执行git diff --cached gr-roi
可以看到在子模块目录之外,主项目git并
安装uhd
安装gnuradio的过程中会安装uhd的一套东西,如果没有,需要去安装。如果没有镜像,则从ettus的官网下载地址下载。这里可以参考在ubuntu下使用指令烧写固件到usrp N210。这里由于已经在安装gnuradio的时候安装过了,我这里安装目录在/usr/local/lib/uhd/utils
连接usrp
为了使得PC和usrp相互连接,需要将这两者的ip设为同一网段。比如我这里有限连接的ip的地址设置为192.168.10.1,子网掩码是24。下面会介绍如何设置usrp的地址(这里设备为N210)。
首先,执行
ruiy2@ruiy2-All-Series:/usr/local/lib/uhd/utils$ uhd_find_devices
[INFO] [UHD] linux; GNU C++ version 5.4.0 20160609; Boost_105800; UHD_3.11.0.1-2-g3939fa90
--------------------------------------------------
-- UHD Device 0
--------------------------------------------------
Device Address:
serial: 306E981
addr: 192.168.40.3
name:
type: usrp2
可以看到这里usrp的ip和pc并不在同一网段下面,所以需要修改usrp的ip。
如果显示错误信息No UHD Devices Found
,可以参考这里去排查问题。可能原因有三点:
- UHD的版本太老,比较新的板子需要下载较新版本的UHD。
- USB方面的原因
- 网络设备不在同一网段
实际上,这里我遇到了第三个问题,导致uhd_find_device
未找到。但是又不知道设备ip之前是什么,所以就把有限连接的子网掩码改为了8,才有了上面的正确的信息。。。之后才能针对该设备烧写固件、改ip带等等操作。
修改ip的操作比较简单,利用uhd安装目录下的工具
/usrp_burn_mb_eeprom --value
find_package的作用
在CMakeLists.txt如果需要使用第三方库,那么需要知道三个东西
去哪里找头文件(.h等) | 对应于GCC的参数 -I |
去哪里找库文件(.so/.lib/.ddl等) | 对应于GCC的参数 -L |
需要链接的库文件名称 | 对应于GCC的参数 -l |
比如我需要链接第三方库curl,那么在CMakeLists.txt中可以书写如下:
include_directories(/usr/include) # 对应于-I target_link_libraries(target curl) # 对应于 -L和-l
find_package的作用就是去寻找该库的头文件位置、库文件位置以及库文件名称,并将其设为变量,返回提供给CMakeLists.txt其他部分使用。
例如:
find_package(curl) include_directories(${CURL_INCLUDE_DIR}) target_link_libraries(curltest ${CURL_LIBRARY})
或者
find_package(CURL) if(CURL_FOUND) include_directories(${CURL_INCLUDE_DIR}) target_link_libraries(curltest ${CURL_LIBRARY}) else(CURL_FOUND) message(FATAL_ERROR "curl not found!") endif(CURL_FOUND)
那么find_package会去 ${CMAKE_MODULE_PATH}的所有路径中去寻找FindCURL.cmake(是的,一般会去查找Find<name>.cmake这种格式),并执行FindCURL.cmake里的代码。
实际上,find_package寻找.cmake文件的搜索规则为
- ${CMAKE_MODULE_PATH},该变量是CMake默认自带的一个路径,也可以通过语句添加自己的CMake的模块搜索路径,如:
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") # CMAKE_SOURCE_DIR是项目根目录
- <CMAKE_ROOT>/share/cmake-x.y/Modu