这是一份使用 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
假设工作目录在 d:\libs,将各个压缩包解压至该目录下。后面编译生成的文件都放在默认位置,需要整理目录的将对应路径替换即可。
加工
gtest
定位到
1 | googletest-release-1.6.0/msvc |
文件夹,打开 getst-md.sln 进行编译,-md 就是动态链接的 crt 库,静态链接就用不带 md 的 sln 文件,别点错了。编译后在
1 | googletest-release-1.6.0/msvc/gtest-md/Debug |
目录下生成 gtestd.lib 文件。
protobuf
定位到
1 | protobuf-2.5.0/vsprojects |
目录,因为和上面 gtest 的项目不同,这里只有一个以 /MD 配置好的解决方案文件,所以上面 gtest 也要选择 /MD 模式的解决方案。打开 protobuf.sln 直接开始编译即可。文件在
1 | protobuf-2.5.0/vsprojects/Debug |
中。估计是项目生成依赖有问题,一次性编译所有项目时部分会失败,多编译几次找到依赖后自然就成功了。
Icu
使用预编译的 vs2010 版,配上 2010 的运行库即可直接食用。
Boost
使用预编译的库,解压后摆好。libboost 的几个压缩包解压出来的 lib 可以整理到一起,boost_1_44_0 作为 include 目录单独放在 d:\libs 目录下即可。
1 | D:\libs\boost_1_44_0\ |
2 | D:\libs\libboost-vc90-mt-gd-1_44\libboost_date_time-vc90-mt-gd-1_44.lib |
3 | D:\libs\libboost-vc90-mt-gd-1_44\libboost_system-vc90-mt-gd-1_44.lib |
4 | 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 路径填入
1 | d:/libs/libphonenumber-8.10.9/cpp |
build binaries 路径填入
1 | d:/libs/libphonenumber-8.10.9/cpp/build(输出目录,也可以填任意其他路径) |
其中 boost 的相关配置项需要手动加上。
1 | BOOST_LIBRARYDIR d:/libs/libboost-vc90-mt-gd-1_44 |
2 | BOOST_INCLUDEDIR d:/libs/boost_1_44_0 |
额外添加 Boost_FIND_REQUIRED 可以使 CMake 在查找 boost 库失败时给出详细提示,方便排查。
之后就能顺利的配置、生成 sln 解决方案了。
点击“configure”后选择编译器为 vs2008(vc90),重复“点击 configure” – “填入配置项”直到配置成功,最后结果如下:
1 | BUILD_GEOCODER √ |
2 | BUILD_STATIC_LIB √ |
3 | Boost_FIND_REQUIRED √ |
4 | CMAKE_CONFIGURATION_TYPES Debug;Release;MinSizeRel;RelWithDebInfo |
5 | CMAKE_INSTALL_PREFIX d:/libs/libphonenumber-8.10.9/cpp/build |
6 | GTEST_INCLUDE_DIR d:/libs/googletest-release-1.6.0/include |
7 | GTEST_LIB d:/libs/googletest-release-1.6.0/msvc/gtest-md/Debug/gtestd.lib |
8 | GTEST_SOURCE_DIR d:/libs/googletest-release-1.6.0/src |
9 | ICU_I18N_INCLUDE_DIR d:/libs/icu4c-4_8_1_1-Win32-msvc10/include |
10 | ICU_I18N_LIB d:/libs/icu4c-4_8_1_1-Win32-msvc10/lib/icuin.lib |
11 | ICU_UC_INCLUDE_DIR d:/libs/icu4c-4_8_1_1-Win32-msvc10/include |
12 | ICU_UC_LIB d:/libs/icu4c-4_8_1_1-Win32-msvc10/lib/icuuc.lib |
13 | JAVA_BIN C:/Program Files/Java/jre1.8.0_201/bin/java.exe |
14 | PROTOBUF_INCLUDE_DIR d:/libs/protobuf-2.5.0/src |
15 | PROTOBUF_LIB d:/libs/protobuf-2.5.0/vsprojects/Debug/libprotobuf.lib |
16 | PROTOC_BIN d:/libs/protobuf-2.5.0/vsprojects/Debug/protoc.exe |
17 | USE_BOOST √(不勾也行,就是没有线程安全性了) |
18 | USE_ICU_REGEXP √ |
19 | USE_ALTERNATE_FORMATS √ |
20 | USE_RE2 x |
21 | USE_STD_MAP x |
22 | USE_LITE_METADATA x |
编译错误
- 编译 geocoding 项目需要的头文件 dirent.h 可在这里下载一份,然后放到
1
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,并在前面加上
1
#include <boost/scoped_ptr.hpp>。
- 提示 phonenumberutil.cc 中 PhoneNumberUtil::kMinLengthForNsn 等类静态常量重定义:将 57~60 行这四个静态常量改为枚举即可。
- phonenumberutil_test.cc 中使用 EXPECT_NE / EXPECT_EQ 宏对迭代器进行比较产生模板推导错误,原因在于模板推断时将迭代器类型推断为容器类型,之后便开始调用它的 begin / end 方法导致错误。
可以改为直接判断结果是否为 true,如将1
EXPECT_NE(types.find(PhoneNumberUtil::FIXED_LINE), types.end());
改为
1
EXPECT_TRUE(types.find(PhoneNumberUtil::FIXED_LINE) != types.end());
或干脆注释掉。 - 还有个 geocoding-shared 的链接错误需要在项目中加入
1
libphonenumber-8.10.9/cpp/src/phonenumbers
目录和
1
libphonenumber-8.10.9/cpp/src/phonenumbers/utf
目录下的 c/cc 文件(但要排除 regexp_adapter_re2.cc、metadata.cc、lite-meta.cc,会导致符号重定义),另外还要加上
1
libphonenumber-8.10.9/cpp/src/phonenumbers/base/strings/string_piece.cc
的引用。
这些都改掉之后就可以顺利编译了。
Ex: 模板推导错误的原因估计在于 vc90 中将构造函数也作为类型参与推导了,结果造成了错误。样例如下,在 vs2008 中两个都会被推导为 1
1 | typedef int IsContainer; |
2 | typedef char IsNotContainer; |
3 |
4 | template < class C> |
5 | IsContainer IsContainerTest( |
6 | int /* dummy */ , |
7 | typename C::iterator* /* it */ = NULL, |
8 | typename C::const_iterator* /* const_it */ = NULL |
9 | ) |
10 | { |
11 | return 1; |
12 | } |
13 |
14 | template < class C> |
15 | IsNotContainer IsContainerTest( |
16 | long /* dummy */ |
17 | ) |
18 | { |
19 | return '\0' ; |
20 | } |
21 |
22 | class con |
23 | { |
24 | public : |
25 | class const_iterator |
26 | { |
27 | public : |
28 | const_iterator() {} |
29 | }; |
30 |
31 | class iterator : public const_iterator |
32 | { |
33 | public : |
34 | iterator() {} |
35 | }; |
36 | }; |
37 |
38 | int main() |
39 | { |
40 | int res1 = IsContainerTest< con::iterator >(0); // vc90 -> 1, vc100 -> 0 |
41 | int res3 = IsContainerTest< con >(0); // vc90 -> 1, vc100 -> 1 |
42 | std::cout << res1<< res3 ; |
43 |
44 | return 0; |
45 | } |
尝菜
1 | #include <iostream> |
2 | #include <string> |
3 |
4 | #include "phonenumbers/phonenumber.h" |
5 | #include "phonenumbers/phonenumberutil.h" |
6 |
7 | using i18n::phonenumbers::PhoneNumberUtil; |
8 | using i18n::phonenumbers::PhoneNumber; |
9 |
10 | int _tmain( int argc, _TCHAR* argv[]) |
11 | { |
12 | PhoneNumberUtil *phoneUtil = PhoneNumberUtil::GetInstance(); |
13 |
14 | //初始化一个电话号码 |
15 | PhoneNumber pn; |
16 | pn.set_country_code(86); |
17 | pn.set_national_number(13478808311); |
18 |
19 | //判断电话号码是否有效 |
20 | std::cout << std::boolalpha << phoneUtil->IsValidNumber(pn) << std::endl; |
21 |
22 | //判断电话号码所在的地区 |
23 | std::string region; |
24 | phoneUtil->GetRegionCodeForNumber(pn, ®ion); |
25 | std::cout << region << std::endl; |
26 |
27 | std::string name; |
28 | phoneUtil->GetNationalSignificantNumber(pn, &name); |
29 | std::cout << name << std::endl; |
30 |
31 | //获取某个国家的国字区号 |
32 | PhoneNumber example; |
33 | phoneUtil->GetInvalidExampleNumber( "CN" , &example); |
34 | std::cout << example.country_code() << std::endl; |
35 |
36 | return 0; |
37 | } |
然后加上 include 目录和附加的 lib 目录,以及导入库。
include
1 | 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
1 | 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
1 | phonenumber.lib gtestd.lib icuin.lib icudt.lib icuuc.lib libprotobuf.lib libboost_thread-vc90-mt-gd-1_44.lib |
运行后可以输出:
1 | true |
2 | CN |
3 | 13478808311 |
4 | 86 |