OpenVolumeMesh编译的一些错误解决
预备
首先去官网下载源代码:http://www.openvolumemesh.org/download/
文件:openvolumemesh-src-1.1.0.tar.gz
之后安装通常的Cmake操作生成相应的工程文件,我这里生成的是VS 2013 (Update 2)的工程。
错误说明
不修改源码直接编译上述生成的工程,最终可以得到OpenVolumeMesh.lib文件(Debug和Release版本的都可以得到,在Cmake时候配置生成两个版本即可)。之后使用下面例子测试该库,具体代码如下:
#include <OpenVolumeMesh\FileManager\FileManager.hh>
#include <iostream>
#include <string>
#include <OpenVolumeMesh/Geometry/VectorT.hh>
// Hexahedral meshes
#include <OpenVolumeMesh/Mesh/HexahedralMesh.hh>
// Polyhedral meshes
#include <OpenVolumeMesh/Mesh/PolyhedralMesh.hh>
using namespace std;
using namespace OpenVolumeMesh;
int main()
{
OpenVolumeMesh::GeometricPolyhedralMeshV3f myMesh;
const string s = "test.poly";
OpenVolumeMesh::IO::FileManager fm;
std::cout << fm.readFile(s, myMesh) << std::endl;
return 0;
}
在VS工程中配置好相应的库文件和头文件之后,直接编译,会提示如下链接错误(
OpenVolumeMesh::IO::FileManager::readFile
)
1>main.obj : error LNK2001: 无法解析的外部符号 "public: bool __thiscall OpenVolumeMesh::IO::FileManager::readFile<class OpenVolumeMesh::GeometryKernel<class OpenVolumeMesh::Geometry::VectorT<float,3>,class OpenVolumeMesh::TopologyKernel> >(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class OpenVolumeMesh::GeometryKernel<class OpenVolumeMesh::Geometry::VectorT<float,3>,class OpenVolumeMesh::TopologyKernel> &,bool,bool)const " (??$readFile@V?$GeometryKernel@V?$VectorT@M$02@Geometry@OpenVolumeMesh@@VTopologyKernel@3@@OpenVolumeMesh@@@FileManager@IO@OpenVolumeMesh@@QBE_NABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AAV?$GeometryKernel@V?$VectorT@M$02@Geometry@OpenVolumeMesh@@VTopologyKernel@3@@2@_N2@Z)
问题解决过程
很长的错误,但是很明显的就是说readFile这个函数不匹配或者未定义,通常情况下这种错误的原因有以下几种:
1. Debug和Release版本的库混用
2. X86和x64的库混用
3. 编译库的编译器版本不同,比如说VS2010编译出的库一般情况是不能用在VS2013的
4. 这个函数仅仅在头文件中声明,但是编译时候没有链接到lib中(这个错误原因属于此)
之后安排排查上述各种错误,最终可以发现前三个没有任何问题,整个编译过程中一直用的同一个环境,没有任何更改。所以应该是最后一种情况。可以去查看源代码,首先找到readFile函数所在的声明头文件FileManager.hh,其声明情况如下
/**
* \brief Read a mesh from a file
*
* Returns true if the file was successfully read. The mesh
* is stored in parameter _mesh. If something goes wrong,
* this function returns false.
*
* @param _filename The file that is to be read
* @param _mesh A reference to an OpenVolumeMesh instance
* @param _topologyCheck Pass true if you want to perform a topology check
* each time an entity is added (slower performance)
* @param _computeBottomUpIncidences Pass true if you want the file manager
* to directly compute the bottom-up incidences
* for the mesh. (Note: These are needed for
* some iterators to work, see documentation)
*/
template <class MeshT>
bool readFile(const std::string& _filename, MeshT& _mesh,
bool _topologyCheck = true,
bool _computeBottomUpIncidences = true) const;
声明没有任何问题,四个参数,但是两个是默认参数的。寻找该函数的实现,在文件FileManagerT.cc中,然后在都头文件的最后又发现如下代码:
#if defined(INCLUDE_TEMPLATES) && !defined(FILEMANAGERT_CC)
#include "FileManagerT.cc"
#endif
错误就在这段代码之中,这段代码的意思是如果定义了INCLUDE_TEMPLATES这个宏并且没有定义FILEMANAGERT_CC(这个是在文件FileManagerT.cc定义,用于避免重定义),问题就出在前一个宏名字上,因为这个宏没有被定义,所以下面这个#include不会被执行,也就是FileManagerT.cc不会被包含在头文件中。而且可以查看到CmakeList.txt(OpenMesh/src/CmakeList.txt)中有下面这一句
# Don't build template cc files as they only contain templates
acg_drop_templates(sources)
这句话的意思是将所有模板函数的文件剔除工程。
这两个设置会造成最终没有包含进函数的实现,仅仅包含了函数的声明。这样的话一种方法是在你使用函数的时候将*T.cc作为头文件使用#include进自己的代码中,但是这种方法很不安全,因为.cc文件中没有实现头文件保护,或许工程小的时候不会出现重定义,但是工程大了难免有重定义出现,因此不推荐使用这种方法解决该问题,而且在这个工程中有很多模板文件都是这么处理,如果这么修改都要修改其他的文件,而且还要非常清楚什么时候引用这些*T.cc文件。
如果直接按照上面的方法试验的话,将*T.cc添加到自己的工程,原始的link错误会消失,但是伴随而来的是一个函数未定义typeName函数未定义,可以查看这个函数又是工程中另一个模板文件中实现的函数,所以这样错误会一连串的出现。
另一种解决办法是,直接将文件都包含在工程中,去掉CmakeList中的那一行,试验证明不能解决问题,原因未明(在下目前还是小白,囧)。我采用了另外一种思路去解决这个问题,因为通常情况下模板写的函数不需要编译链接(或许这个也是上面删除CmakeList中那一行不成功的原因),因此只要能够将这些模板的函数放到相应的头文件中就完全可以了,而且貌似原始作者也是这么个打算,因为,他们在头文件的最后写了一句条件宏,欲将模板直接引进头文件。
但是问题就出现在这里,他试图引进却因为没有定义宏名而失败,所以我试着将整个工程中所有这个宏名都注释或取消掉,只留下如下格式:
#if /*defined(INCLUDE_TEMPLATES) &&*/ !defined(FILEMANAGERT_CC)
#include "FileManagerT.cc"
#endif
这里的!define不能取消,这里它的作用类似于头文件的guid保护。
搜索整个工程中的INCLUDE_TEMPLATES将他们都做类似处理,再次编译链接,会发现链接错误已经消失,问题解决。
分支错误
读取的文件错误,读取的文件是官网提供的例子文件格式如下:
OVM ASCII
Vertices
8
-1.0 -1.0 -1.0
1.0 -1.0 -1.0
1.0 1.0 -1.0
-1.0 1.0 -1.0
-1.0 -1.0 1.0
1.0 -1.0 1.0
1.0 1.0 1.0
-1.0 1.0 1.0
Vertex_Property "Vertex Weights"
float
1.363
6.334
2.766
8.348
4.214
2.136
7.114
0.651
Edges
12
0 1
1 2
2 3
3 0
4 5
5 6
6 7
7 4
0 4
1 5
2 6
3 7
Edge_Property "Edge Tag"
bool
1
1
0
1
1
0
0
1
0
0
1
1
Faces
6
4 0 2 4 6
4 8 10 12 14
4 18 10 21 3
4 16 15 23 6
4 20 12 23 5
4 0 18 9 17
Face_Property "Face Selection"
bool
1
1
0
1
1
0
HalfFace_Property "HalfFace Constraints"
double
1.22354
0.11698
1.83562
0.19378
0.23567
1.23565
1.23567
0.95874
0.43532
2.22457
0.10957
1.09758
Polyhedra
1
6 1 2 5 6 9 10
正常读取该文件的话会提示:No edge section defined!没有成功读取边信息,看文件结构Vertices应该是没有出错,因为提示不是:No vertex section defined!去看看读文件函数会发现,这个读取过程仅仅处理了文件中的Vertices,Edges,Faces以及POLYHEDRA标签,但是文件中海油Vertex_property,Edge_Property,Face_Property, HalfFace_Property属性标签,readFile函数根本没有处理,所以会出现上面没有边的提示。因为读取点的时候一切正常,但是按程序顺序,它期待的下一个是Edges但是出现的却是Vertex_Property,所以就告诉你没有边信息,而且它还直接跳出了函数。所以讲所有的Property属性标签去掉之后,再次读取文件,一切正常。
这里读取的文件格式有些不友好,方法更是暴力省事,应该是可以自己添加读取的函数,但是属性的类型竟然是在文件中指定。所以在读取所有格式的文档时候应该需要考虑这些事情,或者可以也不友好的直接指定。