Gismo库杂谈——与Eigen库的冲突

问题描述

在较新版本的Gismo库中可能会出现与外部Eigen库冲突的问题,例如以下代码:

#include <gismo.h>
#include <Eigen/Dense>

int main()
{
    gismo::gsMatrix<real_t> A(2, 2);
    Eigen::MatrixXd B(2, 2);
    return 0;
}

这段代码在编译阶段会报错:“Eigen”:不是类或命名空间名称。
出现这个问题的原因在于库的创建者为了修复gismo与其他库中的Eigen冲突问题,将包含在gismo中的Eigen命名空间重命名为gsEigen,但其中Eigen库的所有头文件开头的保护性宏定义没有被修改,于是#include <Eigen/Dense>引入的所有头文件的内容都在开头的#ifndef EIGEN_XXX_H判断为非后被忽略掉了。

解决方案

参考官方issue #438中提到的 “Undefine all eigen guards so that loading again Eigen library from another source is possible.”
如果一定要使用外部的Eigen库,则应先取消掉一些宏定义,我们可以在gismo源目录中或安装好的gismo的include目录中打开终端,执行以下命令:

grep -rh "#ifndef EIGEN_.*_H" * | sed "s/#ifndef/#undef/g" > gsUndefEigenGuards.h

需要注意,这行命令涉及的两个程序grepsed都是Unix系统下的,如果想在Windows下使用则需安装额外工具,我这里安装的是BusyBox.
执行了以上的命令后,文件夹中会多出来一个文件gsUndefEigenGuards.h,里面每一行都是#undef EIGEN_XXX_H的形式,这就是在取消掉Eigen的所有保护宏。当然如果这一步实在不会操作,我这边也提供了我输出的gsUndefEigenGuards.h以供使用。(gsUndefEigenGuards.h
在得到该文件后,将其加入到项目中并在#incude <gismo.h>后引入它就可以解决问题了。

#include <gismo.h>
#include "gsUndefEigenGuards.h"
#include <Eigen/Dense>

int main()
{
    gismo::gsMatrix<real_t> A(2, 2);
    Eigen::MatrixXd B(2, 2);
    return 0;
}

替代性方案:
在gismo库中也含有一个内部的Eigen,但是由于Eigen命名空间被修改导致我们无法直接使用,所以如果考虑到前面的解决方案繁琐且存在隐患,也可以通过增加一行#define Eigen gsEigen的方式来使用gismo内部的Eigen

#include <gismo.h>
#define Eigen gsEigen

int main()
{
    gismo::gsMatrix<real_t> A(2, 2);
    Eigen::MatrixXd B(2, 2);
    return 0;
}

补充说明

以上的方案通过在#include <gismo.h>后引入一个取消Eigen保护宏的头文件来解决问题,然而如果Eigen库是在gismo之前引入的呢?那我们则需要在Eigen之后,gismo之前引入这个头文件:

#include <Eigen/Dense>
#include "gsUndefEigenGuards.h"
#include <gismo.h>

当然在复杂的场景下,比如多文件编译,我们需要在合适的位置引入这个头文件,在使其发挥作用的同时还需避免如下所示的在两次重复引入Eigen之间引入此头文件导致Eigen中的内容重定义。

#include <Eigen/Dense>
#include "gsUndefEigenGuards.h"
#include <Eigen/Decse>

参考官方issue #438, #720, #661