标签:cpp

以下是与标签 “cpp” 相关联的文章

在 ubuntu server 上搭建 C++ 远程开发环境

  • 安装开发软件

    apt install build-essential
    apt install clang
    apt install cmake
    apt install git
    apt install subversion
    apt install ninja ninja-build
    apt install gcc-multilib g++-multilib lib32z1-dev #用于 32 位交叉编译
    
  • 安装并配置 samba

    apt install samba smbclient
    cp /etc/samba/smb.conf /etc/samba/smb.conf.bak
    
    emacs /etc/samba/smb.conf
    [share]
      comment = Share Folder require password
      browseable = yes
      path = /home/share
      create mask = 0777
      directory mask = 0777
      valid users = share
      force user = nobody
      force group = nogroup
      public = yes
      writable = yes
      available = yes 
    
    groupadd share -g 6000
    useradd share -u 6000 -g 6000 -s /sbin/nologin -d /dev/null
    smbpasswd -a share
    
    mkdir /home/share
    chmod 777 /home/share
    /etc/init.d/smbd restart
    

C++ do’s and don’ts

do’s

  • 头文件中尽量使用前置声明
  • 可继承类的析构函数一定是虚函数

don’ts

  • 构造函数中不能调用虚函数

QT 的两个链接问题

qt-creator 无法显示链接错误

很早就碰到这个问题,一直没有解决。在 qt-creator compile output 窗口中,仅显示 LINK 错误号,没有错误信息。

用命令行编译看看提示信息是什么。

D:\Qt\Qt5.7.0\5.7\msvc2015\bin\qtenv2.bat
"D:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
cd /d E:\code\qt\my-project
"D:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\nmake.exe" -f Makefile.Debug

看结果发现,LINK 错误信息中有中文,造成解析错误。下载安装 VS2015 英文语言包,安装并切换成英文,可解决此问题。

LINK 错误,提示找不到 signal 和 metaobject。

排除了各种可能,一直无法解决

  • 没有写 Q_OBJECT
  • 命名空间错误
  • 没有链接 obj 文件
  • 删除中间文件重新编译

最后浏览工程文件时发现,头文件和源文件都是 cpp 文件。修改 pro 文件,把头文件修改正确就行了。

找不到链接库问题

微软抓包工具 NetworkMonitor,自带 SDK,包括头文件和库文件。开发中,用到了这个工具。像往常一样,在代码中包含头文件,在链接选项中添加库文件,但是链接一直出错,提示找不到函数定义。

1>Linking...
 unresolved external symbol _NmCloseHandle@4 referenced in function 
 unresolved external symbol _NmCreateFrameParser@12 referenced in function
 unresolved external symbol _NmAddProperty@12 referenced in function 
 unresolved external symbol _NmConfigConversation@12 referenced in function 
 unresolved external symbol _NmCreateFrameParserConfiguration@16 referenced
 unresolved external symbol _NmLoadNplParser@20 referenced in function 
 unresolved external symbol _NmGetPropertyValueById@32 referenced in 
 unresolved external symbol _NmGetPropertyInfo@12 referenced in function
 unresolved external symbol _NmParseFrame@24 referenced in function _wmain
 unresolved external symbol _NmGetFrame@12 referenced in function _wmain
 unresolved external symbol _NmGetFrameCount@8 referenced in function _wmain

是否没有连接到指定文件 NmApi.lib?

代码里用了 #pragma comment(lib, "NmApi.lib") 来指定链接库,没有指定具体目录,是否目录不对?把 NmApi.lib 换了几个目录,没有解决问题。

修改链接方式,在工程的 Link 选项中指定链接文件,带上指定目录,还是不行。试着修改 lib 文件名称,会提示没有找到,说明链接文件是找到了,但是没有找到对应函数实现。

是否头文件或链接库有问题?

检查头文件,用了 extern C 说明接口调用是 C 风格的。lib 文件里有否有对应函数的定义呢,用 dumpblin 查看,显示结果是有定义的。

E:\code\test\TestNmApi\TestNmApi>dumpbin /exports NmApi.lib
Microsoft (R) COFF/PE Dumper Version 14.00.23026.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file NmApi_x64.lib

File Type: LIBRARY

     Exports

       ordinal    name

                  NmAddDriverCaptureFilter
                  NmAddField
                  NmAddFilter
                  NmAddFrame
                  NmAddOlpBlockToFilter
                  ...
                  NmSetNplProfileAttributeByGuid
                  NmStartCapture
                  NmStopCapture

 Summary

         BD .debug$S
         14 .idata$2
         14 .idata$3
          8 .idata$4
          8 .idata$5
          A .idata$6

是否头文件与库文件函数的 name mangling 不对应?

注意到头文件中,函数前都加了修饰符 WINAPI,定义为 __stdcall,不同的修饰符会使用不同的 namemangling,尝试将 WINAPI 重新定义为 __fastcall __cdecl,都不行。__stdcall 的 name mangling 会在函数前加下划线 ,而 lib 库的导出函数并没有下划线!找到问题了,怎么解决呢?

从 DLL 重新生成 LIB

函数定义已经在那里了,只是 lib 定义不对,那么重新生成 liib 是否可以解决问题?从 stackoverflow 上找来一段脚本,可以从 dll 生成 lib 文件。

dumpbin /exports sqlite3.dll > exports.txt
echo LIBRARY SQLITE3 > sqlite3.def
echo EXPORTS >> sqlite3.def
for /f "skip=19 tokens=4" %A in (exports.txt) do echo %A >> sqlite3.def

lib /def:sqlite3.def /out:sqlite3.lib /machine:x86

很顺利地生成了 lib 文件,dumpbin 查看 lib 导出函数,name mangling 也对了,函数前都加上了下划线。启动链接程序,还是出错。发现 name mangling 还是略有不同,导出函数的名称不带参数个数,尝试把 WINAPI 定义为空,再次启动链接,链接成功。但是运行程序直接就崩溃了。

原来是 64 位程序问题

无计可施了,对比 NetMonitor 安装文件,突然注意到有 64 位和 32 位之分。马上想到安装的 64 位版本,而我编译的是 32 位版本,问题应该就在这个。32 位版本在这台 PC 上无法安装,用 7-zip 解压,得到了对应的 lib 文件。再次链接调试,终于成功了。原来 64 位程序的 name mangling 是不带修饰符的。

把 c++当成脚本语言使用

工作中,经常需要批量解析文本。涉及到功能点主要包括:目录递归、文件读写、字符串解析。功能点不多,但很少有人用 C++做这方面的工作,一般认为脚本语言更适合。因为脚本语法表达更简洁,标准库更全面。C++是门严肃的语言,要写冗长的声明,标准库也不全,需要调用平台 API 完成工作,太繁琐。

这几年,C++标准频繁更新,情况不一样了。C++14 增加了 filesystem 标准库,语法也做了简化,几乎可以用来做为脚本语言。

  • 语法简化了,支持 auto 关键字,不用再写很长的 iterator 声明。
  • 目录递归功能,原来只能使用平台相关的 API,现在可以使用 filesystem 库。PS:在 Windows 平台上,MSVC 从 2015 版本开始支持。cygwin g++ 5.3 未支持。mingw g++才更新到 4.9.2 版本,也不支持。
#include <filesystem>
using namespace experimental::filesystem;

void IteratorDir(const path &p)
{
    for (const auto &iter : directory_iterator(p))
    {
        if (is_directory(iter.status()))
        {
            IteratorDir(iter.path());
        }
        else if (is_regular_file(iter.status()))
        {
            if (iter.path().extension() == ".vcproj")
            {
                // cout << iter.path().filename() << endl;
                ParseVcproj(iter.path());
            }
            else if (iter.path().extension() == ".sln")
            {
                SaveSlnContent(iter.path());
            }
        }
    }
}
  • 文件读写一直还是比较方便的
void SaveSlnContent(const path &p)
{
    ifstream file(p.string().c_str());
    ostringstream ss;
    ss << file.rdbuf();
    m_SlnContent = ss.str();
}
  • 字符串解析功能,各种语言差异不大
void ParseSln()
{
    ofstream out("R:\\a.txt");
    out << "digraph G {" << endl
        << "\trankdir = BT;" << endl
        << endl;

    size_t seg = 0;
    while ((seg = m_SlnContent.find("vcproj\", \"", seg)) != string::npos)
    {
        string curProjId = m_SlnContent.substr(seg + 10, 38);
        string curProjName = m_projectIdMap[curProjId];

        size_t seg2 = m_SlnContent.find("EndProject\n", seg);
        string tmp = m_SlnContent.substr(seg + 48, seg2 - seg - 48);
        seg = seg2;

        size_t pos = 0;
        while ((pos = tmp.find("= {", pos)) != string::npos)
        {
            string dependProjId = tmp.substr(pos + 2, 38);
            string dependProjName = m_projectIdMap[dependProjId];
            if (dependProjName.size() && curProjName.size())
                out << "\t\"" << curProjName << "\" -> \"" << dependProjName
                    << "\"" << endl;
            pos += 38;
        }
    }
    out << "}" << endl;
}
  • 编译问题。Sublime Text 3 里,自带 C++编译支持,但仅支持 g++,不支持 MSVC。添加以下配置后,可以支持 msvc 编译。写完代码后,按 C-S-b 调出编译菜单,选择 msvc Run ,直接编译运行,下方弹出窗口显示运行结果。以后再编译运行时,按 C-b 就可以了,不需要再次选择。
{
    "cmd": ["vcvars32.bat", "&", "cl", "/EHsc", "${file}", "1>&2"],
    "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
    "working_dir": "${file_path}",
    "selector": "source.c, source.cpp, source.c++",
    // By default cl is not in your PATH, so add it to your path (preferably)
    // or uncomment "path" and check that it has correct value
    // "path": "C:\\Windows\\System32;C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin",
    // this also will set path for vcvars32.bat
    "shell": true, // Without this sublime has hard times to parse "&" in out command line

    "variants":
    [
        {
            "name": "Run",
            "cmd": ["vcvars32.bat", "&", "cl", "/EHsc", "${file}", "&", "${file_path}/${file_base_name}.exe"]
        }
    ]
}

总结,filesystem 经常用到的函数如下:

#include <filesystem>
using namespace experimental::filesystem;
path();
path.string();
for (const auto &iter : diretory_iterator(path)) {
    iter.path();
    iter.status();
}