1. 程式人生 > >記錄一次從MinGw轉到MSVC編譯器的錯誤經歷

記錄一次從MinGw轉到MSVC編譯器的錯誤經歷

MinGW和MSVC相容度並不那麼好,由於中文的問題,sa一直使用的是MinGW來進行編譯,但說實話,在windows上MinGW編譯出來的程式在體積和速度上和MSVC還是有點差距的,因此,sa最終版打算使用msvc編譯器。
於是,前幾天用Qt5.9 MSVC2015版進行了一下編譯結果發現了許多問題,有語法的問題,也有非常討厭的連結問題。

下面是一些記錄:

  • MinGW比MSVC寬鬆很多,類似於需要返回值得函式,如果不返回值MinGW不會產生錯誤,只會進行警告,MSVC則會直接報錯

  • 編寫庫時,模板類是不能匯出的,我在編寫時並沒留意這點,模板類也進行了匯出,MSVC會報錯,MinGW毫無提示直接忽略,這點MSVC貌似處理更合理

然後遇到了該死的連結問題,首先遇到了莫名其妙的重定義問題error LNK2005

  • 在連結上原本能通過MinGW成功連結,但在MSVC卻出現了error LNK2005錯誤,sa使用的繪圖引擎是qwt,在signACommonUI庫中定義瞭如下函式:

完整程式碼見:master/src/signACommonUI/Chart2D/SAFigureOptCommands.h 的SAFigureReplaceSeriesDataInIndexsCommand

template<typename T,typename TQwtSeries>
class SAFigureReplaceSeriesDataInIndexsCommand
: public SAFigureOptCommand { public: SAFigureReplaceSeriesDataInIndexsCommand(SAChart2D* chart ,QwtSeriesStore<T> *curve ,const QString &cmdName ,const QVector<int
>& inRangIndexs ,const QVector<T>& inRangNewData , QUndoCommand *parent = Q_NULLPTR); SAFigureReplaceSeriesDataInIndexsCommand(SAChart2D* chart ,QwtSeriesStore<T> *curve ,const QString &cmdName ,const QVector<int>& inRangIndexs ,const QVector<T>& inRangOldData ,const QVector<T>& inRangNewData , QUndoCommand *parent = Q_NULLPTR); virtual void redo(); virtual void undo(); private: QVector<T> m_inRangOldData; QVector<int> m_inRangIndexs; QVector<T> m_inRangNewData; QwtSeriesStore<T> *m_curve; }; ... /// /// \brief 序列資料QPointF的替換 /// class SA_COMMON_UI_EXPORT SAFigureReplaceXYSeriesDataInIndexsCommand : public SAFigureReplaceSeriesDataInIndexsCommand<QPointF,QwtPointSeriesData> { public: using SAFigureReplaceSeriesDataInIndexsCommand::SAFigureReplaceSeriesDataInIndexsCommand; }; /// /// \brief 序列資料QwtPlotMultiBarChart的替換 /// class SA_COMMON_UI_EXPORT SAFigureReplaceMultiBarSeriesDataInIndexsCommand : public SAFigureReplaceSeriesDataInIndexsCommand<QwtSetSample,QwtSetSeriesData> { public: using SAFigureReplaceSeriesDataInIndexsCommand::SAFigureReplaceSeriesDataInIndexsCommand; };

連結錯誤提示:

qwtd.lib(qwtd.dll) : error LNK2005: "public: void __thiscall QwtSeriesStore<class QwtSetSample>::setData(class QwtSeriesData<class QwtSetSample> *)" ([email protected][email protected]@@@@[email protected]@@@@@Z) 已經在 SAFigureOptCommands.obj 中定義
....

這裡所有呼叫QwtSeriesStoresetData`處都有連結錯誤提示

看提示就是重定義了QwtSeriesStore<class QwtSetSample>::setData函式,這個函式在模板類的redo()函式裡呼叫:

template<typename T,typename TQwtSeries>
void SAFigureReplaceSeriesDataCommand<T,TQwtSeries>::redo()
{
    ......
    m_curve->setData(new TQwtSeries(curveDatas));
}

完整程式碼見:SAFigureOptCommands.h 的SAFigureReplaceSeriesDataInIndexsCommand

說明編譯器認為我又定義過QwtSeriesStore<class QwtSetSample>::setData函式,看一下資訊,說的是qwtd.lib中的QwtSeriesStoresetData函式在我寫的這個類之後編譯的,也就是說,編譯器先編譯了我寫的這個類,再去看qwtd.lib發現有重複符號。

解決方法就是引用qwtd.lib含有這些例項化模板的類的標頭檔案,讓編譯器知道,這些模板的例項化已經在qwtd.lib中存在了。

終於是把signACommonUI庫編譯完成,接下來到主程式signA,結果又有連結錯誤:LNK2019LNK2001

mainwindow.obj:-1: error: LNK2019: 無法解析的外部符號 "__declspec(dllimport) public: __thiscall SAWaitCursor::SAWaitCursor(void)" ([email protected]@[email protected]),該符號在函式 "private: void __thiscall MainWindow::initPlugin(void)" ([email protected]@@AAEXXZ) 中被引用
SAChartDatasViewWidget.obj:-1: error: LNK2001: 無法解析的外部符號 "__declspec(dllimport) public: __thiscall SAWaitCursor::SAWaitCursor(void)" ([email protected]@[email protected])

一看就是編譯器沒找到SAWaitCursor,具體程式碼見:master/src/signACommonUI/SAWaitCursor.hmaster/src/signACommonUI/SAWaitCursor.cpp

在你看到之前其實只有一個頭檔案:


class SA_COMMON_UI_EXPORT SAWaitCursor
{
public:
    SAWaitCursor()
    {
        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    }
    ~SAWaitCursor()
    {
        QApplication::restoreOverrideCursor();
    }
    void setWaitCursor()
    {
        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    }
    void setCursor(const QCursor cur = QCursor(Qt::WaitCursor))
    {
        QApplication::setOverrideCursor(cur);
    }
    void release()
    {
        QApplication::restoreOverrideCursor();
    }
};


#ifndef SA_SET_AUTO_WAIT_CURSOR
#define SA_SET_AUTO_WAIT_CURSOR() \
    SAWaitCursor __sa__wait__cursor;\
    Q_UNUSED(__sa__wait__cursor);
#endif

略一思索,可能是沒有cpp導致的問題,於是新建對應的cpp檔案,並把在標頭檔案實現的內容寫到cpp中,編譯出來的lib和原來的大小居然也不一樣,這樣順利完成了signA的編譯