继承boost::spirit::qi::grammar的导出类Windows编译失败求助
尝试实现一个继承自boost::spirit::qi::grammar的类并将其导出为动态库,代码如下:
#pragma once #include <impexp.h> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> using boost::spirit::ascii::space_type; struct EXPORT_DLLCLASS Grammar : boost::spirit::qi::grammar< char const*, space_type > { Grammar() : Grammar::base_type( expr ) { } boost::spirit::qi::rule< char const*, space_type > expr, term; };
EXPORT_DLLCLASS会根据平台分别定义为__attribute__( ( visibility( "default" ) ) )(Linux)或__declspec( dllexport )(Windows)。该代码在Linux平台编译成功,但在Windows平台编译时出现如下错误:
C:\.conan\oq2odq\1\include\boost/proto/extends.hpp(536,17): error: call to implicitly-deleted default constructor of 'boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::qi::reference<const boost::spirit::qi::rule<const char *, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type> > >, 0>' : proto_expr_() ^ C:\.conan\oq2odq\1\include\boost/proto/proto_fwd.hpp(377,16): note: in instantiation of member function 'boost::proto::exprns_::extends<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::qi::reference<const boost::spirit::qi::rule<const char *, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type> > >, 0>, boost::spirit::qi::grammar<const char *, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type>, boost::proto::domainns_::default_domain, 0>::extends' requested here struct extends; ^ C:\.conan\oq2odq\1\include\boost/proto/detail/preprocessed/expr_variadic.hpp(50,49): note: default constructor of 'expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::qi::reference<const boost::spirit::qi::rule<const char *, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type> > >, 0>' is implicitly deleted because field 'child0' has no default constructor typedef Arg0 proto_child0; proto_child0 child0;
不添加EXPORT_DLLCLASS则编译正常,请问该如何解决此问题?
问题根源
Windows下__declspec(dllexport)标记整个类时,编译器会强制生成类的默认构造函数、拷贝构造函数等特殊成员函数。但boost::spirit::qi::grammar内部依赖Boost Proto库的类型,这些类型没有默认构造函数,导致编译器无法自动生成所需的特殊成员,从而触发编译错误。Linux平台的可见性标记不会强制生成这些特殊成员,因此没有问题。
具体解决方法
方法1:仅导出类的接口方法而非整个类
将EXPORT_DLLCLASS从类声明移到需要导出的成员函数前,避免编译器尝试生成整个类的特殊成员:
#pragma once #include <impexp.h> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> using boost::spirit::ascii::space_type; struct Grammar : boost::spirit::qi::grammar< char const*, space_type > { EXPORT_DLLCLASS Grammar() : Grammar::base_type( expr ) { } boost::spirit::qi::rule< char const*, space_type > expr, term; };
这种方式仅导出构造函数,减少编译器需要处理的导出内容,避免触发Proto类型的构造问题。
方法2:使用Pimpl模式隔离Boost类型
将包含Boost Spirit类型的实现细节隐藏到内部类中,对外仅导出一个包含指针的轻量级类,彻底避免导出复杂的Boost类型:
头文件(对外接口):
#pragma once #include <impexp.h> struct GrammarImpl; struct EXPORT_DLLCLASS Grammar { Grammar(); ~Grammar(); // 如需对外暴露解析方法,可在此声明并转发到Impl bool parse(const char* input); private: GrammarImpl* pImpl; };
实现文件(内部实现):
#include "Grammar.h" #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> using boost::spirit::ascii::space_type; struct GrammarImpl : boost::spirit::qi::grammar< char const*, space_type > { GrammarImpl() : base_type(expr) { // 这里编写实际的Grammar规则定义 } boost::spirit::qi::rule< char const*, space_type > expr, term; }; Grammar::Grammar() : pImpl(new GrammarImpl()) {} Grammar::~Grammar() { delete pImpl; } bool Grammar::parse(const char* input) { // 转发到Impl的解析逻辑 return boost::spirit::qi::phrase_parse(input, input + strlen(input), *pImpl, space_type()); }
Pimpl模式完全隔离了Boost类型的导出问题,是Windows下导出包含复杂第三方库类型的类的通用解决方案。
内容的提问来源于stack exchange,提问作者t_watcher

