OpenVolumeMesh编译的一些错误解决
预备
首先去官网下载源代码:http://www.openvolumemesh.org/download/
文件:openvolumemesh-src-1.1.0.tar.gz
之后安装通常的Cmake操作生成相应的工程文件,我这里生成的是VS 2013 (Update 2)的工程。
错误说明
不修改源码直接编译上述生成的工程,最终可以得到OpenVolumeMesh.lib文件(Debug和Release版本的都可以得到,在Cmake时候配置生成两个版本即可)。之后使用下面例子测试该库,具体代码如下:
[cpp] view plaincopyprint?
- #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工程中配置好相应的库文件和头文件之后,直接编译,会提示如下链接错误(
[cpp] view plaincopyprint?
- OpenVolumeMesh::IO::FileManager::readFile
)
[cpp] view plaincopyprint?
- 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,其声明情况如下
[cpp] view plaincopyprint?
- /**
- * \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中,然后在都头文件的最后又发现如下代码:
[cpp] view plaincopyprint?
- #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)中有下面这一句
[cpp] view plaincopyprint?
- # 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中那一行不成功的原因),因此只要能够将这些模板的函数放到相应的头文件中就完全可以了,而且貌似原始作者也是这么个打算,因为,他们在头文件的最后写了一句条件宏,欲将模板直接引进头文件。
但是问题就出现在这里,他试图引进却因为没有定义宏名而失败,所以我试着将整个工程中所有这个宏名都注释或取消掉,只留下如下格式:
[cpp] view plaincopyprint?
- #if /*defined(INCLUDE_TEMPLATES) &&*/ !defined(FILEMANAGERT_CC)
- #include "FileManagerT.cc"
- #endif
这里的!define不能取消,这里它的作用类似于头文件的guid保护。
搜索整个工程中的INCLUDE_TEMPLATES将他们都做类似处理,再次编译链接,会发现链接错误已经消失,问题解决。
分支错误
读取的文件错误,读取的文件是官网提供的例子文件格式如下:
[cpp] view plaincopyprint?
- 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属性标签去掉之后,再次读取文件,一切正常。
这里读取的文件格式有些不友好,方法更是暴力省事,应该是可以自己添加读取的函数,但是属性的类型竟然是在文件中指定。所以在读取所有格式的文档时候应该需要考虑这些事情,或者可以也不友好的直接指定。