这是一份使用 vs2008 和 CMake 料理 libphonenumber 的食谱。

准备原料

  • vs2008
  • CMake-3.14.2 下载
  • libphonenumber-release-8.10.9 下载
  • protobuf-release-2.5.0 下载
  • icu4c-4.8.1 下载
  • googletest-release-1.6.0 下载
  • Java Runtime:jre-8u201-windows-x64.exe 下载
  • boost
    • boost_1_44_0.7z 下载
    • libboost_date_time-vc90-mt-gd-1_44.zip 下载
    • libboost_system-vc90-mt-gd-1_44.zip 下载
    • libboost_thread-vc90-mt-gd-1_44.zip 下载

假设工作目录在 d:\libs,将各个压缩包解压至该目录下。后面编译生成的文件都放在默认位置,需要整理目录的将对应路径替换即可。

加工

gtest

定位到

googletest-release-1.6.0/msvc

文件夹,打开 getst-md.sln 进行编译,-md 就是动态链接的 crt 库,静态链接就用不带 md 的 sln 文件,别点错了。编译后在

googletest-release-1.6.0/msvc/gtest-md/Debug

目录下生成 gtestd.lib 文件。

protobuf

定位到

protobuf-2.5.0/vsprojects

目录,因为和上面 gtest 的项目不同,这里只有一个以 /MD 配置好的解决方案文件,所以上面 gtest 也要选择 /MD 模式的解决方案。打开 protobuf.sln 直接开始编译即可。文件在

protobuf-2.5.0/vsprojects/Debug

中。估计是项目生成依赖有问题,一次性编译所有项目时部分会失败,多编译几次找到依赖后自然就成功了。

Icu

使用预编译的 vs2010 版,配上 2010 的运行库即可直接食用。

Boost

使用预编译的库,解压后摆好。libboost 的几个压缩包解压出来的 lib 可以整理到一起,boost_1_44_0 作为 include 目录单独放在 d:\libs 目录下即可。

D:\libs\boost_1_44_0\
D:\libs\libboost-vc90-mt-gd-1_44\libboost_date_time-vc90-mt-gd-1_44.lib
D:\libs\libboost-vc90-mt-gd-1_44\libboost_system-vc90-mt-gd-1_44.lib
D:\libs\libboost-vc90-mt-gd-1_44\libboost_thread-vc90-mt-gd-1_44.lib

不同 crt 库链接方式对应的 boost 库:

  • MTd = mt-s
  • MT = mt-sgd
  • MDd = mt-gd
  • MD = mt

详细可以看这里

另外也可在 CMake 中分别指定 Boost_LIBRARY_DIR_RELEASE 和 Boost_LIBRARY_DIR_DEBUG 来代替单一的 BOOST_LIBRARYDIR。

libphonenumber

打开cmake-gui.exe,source code 路径填入

d:/libs/libphonenumber-8.10.9/cpp

build binaries 路径填入

d:/libs/libphonenumber-8.10.9/cpp/build(输出目录,也可以填任意其他路径)

其中 boost 的相关配置项需要手动加上。

BOOST_LIBRARYDIR    d:/libs/libboost-vc90-mt-gd-1_44
BOOST_INCLUDEDIR    d:/libs/boost_1_44_0

额外添加 Boost_FIND_REQUIRED 可以使 CMake 在查找 boost 库失败时给出详细提示,方便排查。
之后就能顺利的配置、生成 sln 解决方案了。

点击“configure”后选择编译器为 vs2008(vc90),重复“点击 configure” – “填入配置项”直到配置成功,最后结果如下:

BUILD_GEOCODER              √
BUILD_STATIC_LIB            √
Boost_FIND_REQUIRED         √
CMAKE_CONFIGURATION_TYPES   Debug;Release;MinSizeRel;RelWithDebInfo
CMAKE_INSTALL_PREFIX        d:/libs/libphonenumber-8.10.9/cpp/build
GTEST_INCLUDE_DIR           d:/libs/googletest-release-1.6.0/include
GTEST_LIB                   d:/libs/googletest-release-1.6.0/msvc/gtest-md/Debug/gtestd.lib
GTEST_SOURCE_DIR            d:/libs/googletest-release-1.6.0/src
ICU_I18N_INCLUDE_DIR        d:/libs/icu4c-4_8_1_1-Win32-msvc10/include
ICU_I18N_LIB                d:/libs/icu4c-4_8_1_1-Win32-msvc10/lib/icuin.lib
ICU_UC_INCLUDE_DIR          d:/libs/icu4c-4_8_1_1-Win32-msvc10/include
ICU_UC_LIB                  d:/libs/icu4c-4_8_1_1-Win32-msvc10/lib/icuuc.lib
JAVA_BIN                    C:/Program Files/Java/jre1.8.0_201/bin/java.exe
PROTOBUF_INCLUDE_DIR        d:/libs/protobuf-2.5.0/src
PROTOBUF_LIB                d:/libs/protobuf-2.5.0/vsprojects/Debug/libprotobuf.lib
PROTOC_BIN                  d:/libs/protobuf-2.5.0/vsprojects/Debug/protoc.exe
USE_BOOST                   √(不勾也行,就是没有线程安全性了)
USE_ICU_REGEXP              √
USE_ALTERNATE_FORMATS       √
USE_RE2                     x
USE_STD_MAP                 x
USE_LITE_METADATA           x

编译错误

  • 编译 geocoding 项目需要的头文件 dirent.h 可在这里下载一份,然后放到
    libphonenumber-8.10.9/tools/cpp/src

    目录下。

  • geocoding 和 generate_geocoding_data 项目的预处理器定义中加入 COMPILER_MSVC 来避免找不到头文件 stdint.h 的错误
  • log10 二义性:改成 log10f。
  • 编译时跳 (unsigned)(c + 1) <= 256 断言失败(Debug):在 generate_geocoding_data.cc L.189 和 L.190 处将 std::isspace(XXX) 改为 std::isspace((int)(unsigned char)XXX)。
  • 因为 vs2008 不支持 c++11,编译时 phonenumbermatcher.cc 中用到了 std::unique_ptr 会有报错,将 705 行处 std::unique_ptr 改为 boost::scoped_ptr,并在前面加上
    #include <boost/scoped_ptr.hpp>。
  • 提示 phonenumberutil.cc 中 PhoneNumberUtil::kMinLengthForNsn 等类静态常量重定义:将 57~60 行这四个静态常量改为枚举即可。
  • phonenumberutil_test.cc 中使用 EXPECT_NE / EXPECT_EQ 宏对迭代器进行比较产生模板推导错误,原因在于模板推断时将迭代器类型推断为容器类型,之后便开始调用它的 begin / end 方法导致错误。
    可以改为直接判断结果是否为 true,如将

    EXPECT_NE(types.find(PhoneNumberUtil::FIXED_LINE), types.end());

    改为

    EXPECT_TRUE(types.find(PhoneNumberUtil::FIXED_LINE) != types.end());

    或干脆注释掉

  • 还有个 geocoding-shared 的链接错误需要在项目中加入
    libphonenumber-8.10.9/cpp/src/phonenumbers

    目录和

    libphonenumber-8.10.9/cpp/src/phonenumbers/utf

    目录下的 c/cc 文件(但要排除 regexp_adapter_re2.cc、metadata.cc、lite-meta.cc,会导致符号重定义),另外还要加上

    libphonenumber-8.10.9/cpp/src/phonenumbers/base/strings/string_piece.cc

    的引用。

这些都改掉之后就可以顺利编译了。

Ex: 模板推导错误的原因估计在于 vc90 中将构造函数也作为类型参与推导了,结果造成了错误。样例如下,在 vs2008 中两个都会被推导为 1

typedef int IsContainer;
typedef char IsNotContainer;

template <class C>
IsContainer IsContainerTest(
  int /* dummy */,
  typename C::iterator* /* it */ = NULL,
  typename C::const_iterator* /* const_it */ = NULL
  )
{
  return 1;
}

template <class C>
IsNotContainer IsContainerTest(
  long /* dummy */
  )
{
  return '\0';
}

class con
{
public:
  class const_iterator
  {
  public:
    const_iterator() {}
  };

  class iterator : public const_iterator
  {
  public:
    iterator() {}
  };
};

int main()
{
  int res1 = IsContainerTest< con::iterator >(0); // vc90 -> 1, vc100 -> 0
  int res3 = IsContainerTest< con >(0);           // vc90 -> 1, vc100 -> 1
  std::cout << res1<< res3 ;

  return 0;
}

尝菜

#include <iostream>
#include <string>

#include "phonenumbers/phonenumber.h"
#include "phonenumbers/phonenumberutil.h"

using i18n::phonenumbers::PhoneNumberUtil;
using i18n::phonenumbers::PhoneNumber;

int _tmain(int argc, _TCHAR* argv[])
{
    PhoneNumberUtil *phoneUtil = PhoneNumberUtil::GetInstance();

    //初始化一个电话号码
    PhoneNumber pn;
    pn.set_country_code(86);
    pn.set_national_number(13478808311);

    //判断电话号码是否有效
    std::cout << std::boolalpha << phoneUtil->IsValidNumber(pn) << std::endl;

    //判断电话号码所在的地区
    std::string region;
    phoneUtil->GetRegionCodeForNumber(pn, &region);
    std::cout << region << std::endl;

    std::string name;
    phoneUtil->GetNationalSignificantNumber(pn, &name);
    std::cout << name << std::endl;

    //获取某个国家的国字区号
    PhoneNumber example;
    phoneUtil->GetInvalidExampleNumber("CN", &example);
    std::cout << example.country_code() << std::endl;

    return 0;
}

然后加上 include 目录和附加的 lib 目录,以及导入库。
include

d:/libs/icu4c-4_8_1_1-Win32-msvc10/include;d:/libs/libphonenumber-8.10.9/cpp/src;d:/libs/protobuf-2.5.0/src;d:/libs/boost_1_44_0;

libs

d:/libs/protobuf-2.5.0/vsprojects/Debug;d:/libs/icu4c-4_8_1_1-Win32-msvc10/lib;d:/libs/googletest-release-1.6.0/msvc/gtest-md/Debug;D:/libs/boost-1.44-vc90-mt-gd-lib;d:/libs/libphonenumber-8.10.9/cpp/build/Debug;

import

phonenumber.lib gtestd.lib icuin.lib icudt.lib icuuc.lib libprotobuf.lib libboost_thread-vc90-mt-gd-1_44.lib

运行后可以输出:

true
CN
13478808311
86