如何优雅的解析多维字符串到指定类型

有时候会有从数据库或者其他地方拿到一组数据,全是字符串类型,然后需要自己根据实际需要的使用类型去进行转换。(我遇到这个恰好是在mysql查询读出来的数据时,是个二维的字符串数组)。

先引出最简单的一种情况,明确知道将要解析出来的类型和个数。通常就直接如下解决。

1
2
3
4
std::array<std::string, 2> iDataArray;
iDataArray[0] = "1";
iDataArray[1] = "dandiao";
cout << "1 user_id = " << std::stoi(iDataArray[0]) << ", user_nick = " << iDataArray[1] << endl;

解决方案

现在,假设上面数据中iDataArray里的两个字段实际要转换的类型变了,就需要重新写一段类似的代码。为了解决这个问题,自然想到将参数类型模版化,来达到通用的效果,且看代码:

1
2
3
4
5
6
7
template <typename T1, typename T2>
void Decode(std::array<std::string, 2>& rikDataArray, T1& paramer1, T2& paramer2)
{

// 这里还要考虑将字符串类型转换成哪一种类型,还好boost提供了一个通用的模板函数
paramer1 = boost::lexical_cast<T1>(rikDataArray[0]);
paramer2 = boost::lexical_cast<T2>(rikDataArray[1]);
}

上面的代码利用了模板参数来传入参数类型和顺序信息,解决了上面提到的问题。这里引入了boost::lexical_cast这个模板函数,它可以将传入的参数以你指定的类型返回。当然我们这里其实都是将字符串转换成指定类型。
调用方式如下:

1
2
3
4
int nID = 0;
std::string iNickStr;
Decode<int, std::string>(iDataArray, nID, iNickStr);
cout << "2 user_id = " << nID << ", user_nick = " << iNickStr << endl;

但是,上面的方法有个特别明显的缺点:限定了只能解析两个字段。

使用模板推导过程实现通用

对于源数据字段数的限制,用大小可变的容器vector来代替array即可。
对于模板参数的个数,自然想到用c++11的可变参数模版。

1
2
3
4
5
6
7
8
9
10
11
12
13
template <typename ... Parameters>
bool Decode(std::vector<std::string>& rikDataVector, Parameters... args)
{

if(rikDataVector.empty() || sizeof...(Parameters) != rikDataVector.size())
{
std::cout << "data src not equal cout of paramer" << std::endl;
return false;
}

// 为了避免拷贝std::vector<std::string>,增加一个索引参数记录当前处理到的索引。或者用其他方式传指定索引开始的引用内容进去
size_t nIndex = 0;
return DecodeDetail(rikDataVector, nIndex, args...);
}

代码中函数DecodeDetail是利用了模板推导过程逐个剥离出参数来转换值。相关函数实现为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <cassert>
template<typename T>
bool DecodeDetail(const std::vector<std::string>& rikDataVector, size_t& rnIndex, T& rValue)
{

assert(rnIndex == rikDataVector.size() - 1);
if(!Convert(rikDataVector[rnIndex], rValue))
{
std::cout << "convert index = " << rnIndex << ", value = " << rikDataVector[rnIndex] << " to type = " << typeid(rValue).name() << " failed";
return false;
}

return true;
}


template<typename T1, typename... Parameters>
bool DecodeDetail(const std::vector<std::string>& rikDataVector, size_t& rnIndex, T1& rValue, Parameters&... args)
{

if(rikDataVector.size() - rnIndex - 1 != sizeof...(Parameters))
{
std::cout << "data src not equal cout of paramer" << std::endl;
return false;
}

if(!Convert(rikDataVector[rnIndex], rValue))
{
std::cout << "convert index = " << rnIndex << ", value = " << rikDataVector[rnIndex].c_str() << " to type = " << typeid(rValue).name() << " failed";
return false;
}

return DecodeDetail(rikDataVector, ++rnIndex, args...);
}

这里代码里出现了一个Convert函数,传入转换源和目标参数,返回转换是否成功。

Convert的两种实现方法

这里其实可以直接使用boost::lexical_cast进行转换来达到效果,我这里之所以封装成函数,是因为有两种方案去实现Convert。
下面是方案1:使用stringstream,加上对string的特例化处理达到效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 缺点,对于float类型的字符串“2.2”转换成int类型时,不会报错。
#include <sstream>
template<typename TypeDst, typename TypeSrc>
bool Convert(const TypeSrc& rikSrc, TypeDst& rValue)
{

if(!(std::istringstream(rikSrc) >> rValue))
{
return false;
}
return true;
}
// 模板特例化:解决源字符串中带空格或者回车时的转换问题
template<>
bool Convert(const std::string& rikSrcStr, std::string& rValue)
{

rValue = rikSrcStr;
return true;
}

下面是方案2:封装使用boost::lexical_cast

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <boost/lexical_cast.hpp>
template<typename TypeDst, typename TypeSrc>
bool Convert(const TypeSrc& rikSrc, TypeDst& rValue)
{

try
{
rValue = boost::lexical_cast<TypeDst>(rikSrc);
}
catch(const std::exception& rikException)
{
return false;
}
return true;
}

以上两种Convert的实现方案都有效,其实boost::lexical_cast内部实现就是使用输入输出流,只不过做了更多的情况处理。

测试

是时候测试一下了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int nInt = 0;
std::string iStr;
float fFloat = 0.0f;
bool bBool = false;

// 测试1: int, string
std::cout << "测试1: int, string ==> " << std::endl;
std::vector<std::string> iDataVector1{"1", "test1"};
if(false != Decode(iDataVector1, nInt, iStr))
{
std::cout << "bBool = " << bBool << ", nInt = " << nInt << ", fFloat = " << fFloat << ", iStr = " << iStr << std::endl;
}
else
{
std::cout << "test1 decode error" << std::endl;
}
std::cout << std::endl;

// 测试2: float, string , int
std::cout << "测试2: float, string , int ==> " << std::endl;
std::vector<std::string> iDataVector2{
"2.222", "test2", "2"};
if(false != Decode(iDataVector2, fFloat, iStr, nInt))
{
std::cout << "bBool = " << bBool << ", nInt = " << nInt << ", fFloat = " << fFloat << ", iStr = " << iStr << std::endl;
}
else
{
std::cout << "test2 decode error" << std::endl;
}
std::cout << std::endl;

// 测试3: string, bool, int, float
std::cout << "测试3: string, bool, int, float ==> " << std::endl;
std::vector<std::string> iDataVector3{"test3", "1", "3", "3.333"};
if(false != Decode(iDataVector3, iStr, bBool, nInt, fFloat))
{
std::cout << "bBool = " << bBool << ", nInt = " << nInt << ", fFloat = " << fFloat << ", iStr = " << iStr << std::endl;
}
else
{
std::cout << "test3 decode error" << std::endl;
}
std::cout << std::endl;

下面是测试结果:

1
2
3
4
5
6
7
8
测试1int, string ==> 
bBool = 0, nInt = 1, fFloat = 0, iStr = test1

测试2float, string , int ==>
bBool = 0, nInt = 2, fFloat = 2.222, iStr = test2

测试3string, bool, int, float ==>
bBool = 1, nInt = 3, fFloat = 3.333, iStr = test3

到了这里似乎已经解决问题了。

应对多维的解析

上面的方案只适用一维的呀,如果是二维的怎么办,只能提供一个容易来包装每组一维数据对应的参数了。比如使用一个结构体对应存放一个一维的解析数据,然后使用std::list来容纳这些结构体。

但是对于不同的形参组合,需要定义不同的结构体,比较麻烦。幸运的是,c++11提供的tuple在这种情况下提供了灵活的方案。

我们先看对于一维的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

#ifdef WIN32 // LINUX
template<typename ... Parameters>
std::tuple<Parameters ...> MakeTuple(Parameters ... args)
{
return std::make_tuple(args...);
}
#endif

template<typename... Parameters>
bool Decode(const std::vector<std::string>& rikDataVector, std::tuple<Parameters...>& riParamerTuple)
{

if(rikDataVector.empty() || sizeof...(Parameters) != rikDataVector.size())
{
std::cout << "data src not equal cout of paramer" << std::endl;
return false;
}

try
{
size_t nIndex = rikDataVector.size() - 1;
#ifndef WIN32 // CentOS7.0下GCC4.8测试正常,其他的没有测试。
riParamerTuple = std::make_tuple(boost::lexical_cast<Parameters>(rikDataVector[nIndex--])...);
#else
riParamerTuple = MakeTuple(boost::lexical_cast<Parameters>(rikDataVector[nIndex--])...);
#endif
// 这里要注意两点:
/* 1、为什么要用--运算符来逆序传入数据源?
考虑int i = 0; printf("%d,%d,%d", i++, i++, i++); 输出2,1,0 。因为默认的函数调用约定函数参数压栈顺序为从右向左,
所以为了确保传入的参数顺序是从第一个开始进行转换,就要使得传入的源数据顺序为 rikDataVector[0],rikDataVector[1],rikDataVector[3]...。
因此要用--。
*/

/* 2、为什么WIN32下要用函数MakeTuple来封装std::make_tuple?(以下测试均在VS2013中)。
因为实际测试中发现,直接使用std::make_tuple和boost::lexical_cast以及解变参模板运算符'...'的组合使用,会存在问题。
比如使用两个参数的测试数据时:
size_t nIndex = rikDataVector.size() - 1; // 这里测试数据是1
std::tuple<Parameters...> iTuple2((boost::lexical_cast<Parameters>(rikDataVector[nIndex--]))...);
// Parameters... = int, string 等效为 lexical_cast<string>(rikDataVector[0]), lexical_cast<int>(rikDataVector[1]) 会异常
// parameters... = string, int 等效为 lexical_cast<string>(rikDataVector[0]), lexical_cast<int>(rikDataVector[1])
// parameters... = int, float 等效为 lexical_cast<float>(rikDataVector[0]), lexical_cast<int>(rikDataVector[1]) 会异常
// parameters... = float, int 等效为 lexical_cast<float>(rikDataVector[0]), lexical_cast<int>(rikDataVector[1])
// parameters... = string, float 等效为 lexical_cast<string>(rikDataVector[0]), lexical_cast<float>(rikDataVector[1])
// parameters... = float, string 等效为 lexical_cast<string>(rikDataVector[0]), lexical_cast<float>(rikDataVector[1]) 会异常

没有去测试类型更多的结果,但是当我用如下Print函数类型推导输出全部参数时又没有问题。
// 打印变参模板形参值
template<typename T1>
void Print(T1&& rnValue)
{
std::cout << rnValue << std::endl;
}

template<typename T1, typename... Args>
void Print(T1&& rnValue, Args&&... args) // int, float, string
{
Print(rnValue);
Print(args...);
}

Print((boost::lexical_cast<Parameters>(rikDataVector[nIndex--]))...); // 没有问题。

综合比较:
Print((boost::lexical_cast<Parameters>(rikDataVector[nIndex--]))...);
std::tuple<Parameters...> iTuple2((boost::lexical_cast<Parameters>(rikDataVector[nIndex--]))...);

区别就是Print是我自定义的函数,tuple()是标准库函数。没有找到为什么会不同的原因。
但能想到也用一个函数去封装std::make_tuple,因此才有了MakeTuple函数。

// 附带MakeTuple测试结果:
// Parameters... = int, string 等效为 lexical_cast<int>(rikDataVector[0]), lexical_cast<string>(rikDataVector[1])
// parameters... = string, int 等效为 lexical_cast<string>(rikDataVector[0]), lexical_cast<int>(rikDataVector[1])
// parameters... = int, float 等效为 lexical_cast<int>(rikDataVector[0]), lexical_cast<float>(rikDataVector[1])
// parameters... = float, int 等效为 lexical_cast<float>(rikDataVector[0]), lexical_cast<int>(rikDataVector[1])
// parameters... = string, float 等效为 lexical_cast<string>(rikDataVector[0]), lexical_cast<float>(rikDataVector[1])
// parameters... = float, string 等效为 lexical_cast<float>(rikDataVector[0]), lexical_cast<string>(rikDataVector[1])

当然!还有另一种解决方法,就是用一个函数包装lexical_cast的转换,包装代码如下:
template<typename TypeDst, typename TypeSrc>
TypeDst Convert(const TypeSrc& rikSrc)
{
return boost::lexical_cast<TypeDst>(rikSrc);
}
*/

}
catch(const std::exception& rikException)
{
std::cout << "decode failed when user boost::lexical_cast and tuple" << std::endl;
return false;
}

return true;
}

很抱歉这段代码里有那么多的注释,只是我想更好的去描述这个’VS编译器的bug’:make_tuple和lexical_cast配合变参模板解包一起使用时,会出问题!

如果你不想看上面那么一大堆的注释,我这里给你总结一下,注意以下两点:

  1. 注意函数参数的压栈顺序是从右向左的,所以要使用从后向前取数据源。
  2. 注意WINDOWS下,VS编译器下,make_tuple和lexical_cast配合变参模板解包一起使用时,其转换类型顺序无规律可寻,就是有问题。可以使用函数包装make_tuple或者lexical_cast来解决这个问题!

其实除去注释部分,代码量只有区区几行而已。如果是GCC编译器,甚至连上面的MakeTuple都不需要。怎么样,够优雅了吧?

我们看看测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 测试4: bool, int, float, string
std::cout << "测试4: bool, int, float, string ==> " << std::endl;
std::vector<std::string> iDataVector4{"0", "4", "4.444", "test4"};
std::tuple<bool, int, float, std::string> iParamerTuple4;
if(false != Decode(iDataVector4, iParamerTuple4))
{
std::cout << "tuple<0> = " << std::get<0>(iParamerTuple4) << ", tuple<1> = " << std::get<1>(iParamerTuple4)
<< ", tuple<2> = " << std::get<2>(iParamerTuple4) << ", tuple<3> = " << std::get<3>(iParamerTuple4) << std::endl;
}
else
{
std::cout << "test4 decode error" << std::endl;
}
std::cout << std::endl;

// 测试5: float, string, int,
std::cout << "测试5: float, string, int ==> " << std::endl;
std::vector<std::string> iDataVector5{"5.55", "test5", "555"};
std::tuple<float, std::string, int> iParamerTuple5;
if(false != Decode(iDataVector5, iParamerTuple5))
{
std::cout << "tuple<0> = " << std::get<0>(iParamerTuple5) << ", tuple<1> = " << std::get<1>(iParamerTuple5)
<< ", tuple<2> = " << std::get<2>(iParamerTuple5) << std::endl;
}
else
{
std::cout << "test5 decode error" << std::endl;
}
std::cout << std::endl;

// 测试6: string, int,
std::cout << "测试6: string, int ==> " << std::endl;
std::vector<std::string> iDataVector6{"test6", "66"};
std::tuple<std::string, int> iParamerTuple6;
if(false != Decode(iDataVector6, iParamerTuple6))
{
std::cout << "tuple<0> = " << std::get<0>(iParamerTuple6) << ", tuple<1> = " << std::get<1>(iParamerTuple6) << std::endl;
}
else
{
std::cout << "test6 decode error" << std::endl;
}
std::cout << std::endl;

// 测试7: int, string
std::cout << "测试7: int, string ==> " << std::endl;
std::vector<std::string> iDataVector7{"77", "test7"};
std::tuple<int, std::string> iParamerTuple7;
if(false != Decode(iDataVector7, iParamerTuple7))
{
std::cout << "tuple<0> = " << std::get<0>(iParamerTuple7) << ", tuple<1> = " << std::get<1>(iParamerTuple7) << std::endl;
}
else
{
std::cout << "test7 decode error" << std::endl;
}
std::cout << std::endl;

特地多测试了几组数据,因为之前测试情况少了出了很大问题!结果为:

1
2
3
4
5
6
7
8
9
10
11
测试4: bool, int, float, string ==> 
tuple<0> = 0, tuple<1> = 4, tuple<2> = 4.444, tuple<3> = test4

测试5: float, string, int ==>
tuple<0> = 5.55, tuple<1> = test5, tuple<2> = 555

测试6: string, int ==>
tuple<0> = test6, tuple<1> = 66

测试7: int, string ==>
tuple<0> = 77, tuple<1> = test7

二维的解析

是时候上二维解析的代码啦,有了上面一维的,二维的就很简单了,这里连着测试代码一起放上吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 注意:请自行确保rikData2DVector中每个一维数组中要解析的类型顺序是相同的。
template<typename... Parameters>
bool Decode(const std::vector<std::vector<std::string>>& rikData2DVector, std::vector<std::tuple<Parameters...>>& riParamerTupleVector)
{

for(size_t i = 0; i < rikData2DVector.size(); ++i)
{
std::tuple<Parameters...> iTmpTuple;
if(!Decode(rikData2DVector[i], iTmpTuple))
{
std::cout << "data src group[" << i << "] decode error!" << std::endl;
return false;
}

riParamerTupleVector.push_back(iTmpTuple);
}

return true;
}

// 测试8: {int, string},{int,string}{int, string}
std::cout << "测试8: {int, string},{int,string}{int, string} ==> " << std::endl;
std::vector<std::vector<std::string>> iData2DVector8{{"881", "test81"}, {"882", "test82"}};
std::vector<std::tuple<int, std::string>> iParamerTupleVector8;
if(false != Decode(iData2DVector8, iParamerTupleVector8))
{
for(const std::tuple<int, std::string>& rikParamerTuple : iParamerTupleVector8)
{
std::cout << "tuple<0> = " << std::get<0>(rikParamerTuple) << ", tuple<1> = " << std::get<1>(rikParamerTuple) << std::endl;
}
}
else
{
std::cout << "test8 decode error" << std::endl;
}
std::cout << std::endl;

测试结果为:

1
2
3
测试8: {int, string},{int,string}{int, string} ==> 
tuple<0> = 881, tuple<1> = test81
tuple<0> = 882, tuple<1> = test82

嗯,因为二维就是对一维的重复调用,所以这里测试代码我就从简啦。

总结

本来要讲解的很少的,但是因为VS编译器的那个问题,分析测试了好久,然后又多了使用对lexical_cast模板的模拟实现等,这篇博客就痈肿起来了。

不过总算要终结啦,考虑到这份代码主要是讲解这个思路,还有待优化的地方,并且复用性也不是特别大,所以就不上传github了。这里直接贴上吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
#include <iostream>
#include <string>
#include <tuple>
#include <vector>

//#define CONVERT_METHOD_STRINGSTREAM // 使用stringstream做转换

#ifdef CONVERT_METHOD_STRINGSTREAM
//////////////////////////////////////////////////////////////////////////
// 使用istringstream做转换
// 缺点:1、对于"1.2",转成int类型不会报错,直接返回1

#include <sstream>
template<typename TypeDst, typename TypeSrc>
bool Convert(const TypeSrc& rikSrc, TypeDst& rValue)
{

if(!(std::istringstream(rikSrc) >> rValue))
{
return false;
}
return true;
}

// 模板特例化
template<>
bool Convert(const std::string& rikSrcStr, std::string& rValue) // 解决源字符串中带空格或者回车时的转换问题
{

rValue = rikSrcStr;
return true;
}

#else

//////////////////////////////////////////////////////////////////////////
// 使用boost::lexical_cast做转换
#include <boost/lexical_cast.hpp>
template<typename TypeDst, typename TypeSrc>
bool Convert(const TypeSrc& rikSrc, TypeDst& rValue)
{

try
{
rValue = boost::lexical_cast<TypeDst>(rikSrc);
}
catch(const std::exception& rikException)
{
return false;
}
return true;
}

#endif

#include <cassert>
template<typename T>
bool DecodeDetail(const std::vector<std::string>& rikDataVector, size_t& rnIndex, T& rValue)
{

assert(rnIndex == rikDataVector.size() - 1);
if(!Convert(rikDataVector[rnIndex], rValue))
{
std::cout << "convert index = " << rnIndex << ", value = " << rikDataVector[rnIndex] << " to type = " << typeid(rValue).name() << " failed";
return false;
}

return true;
}


template<typename T1, typename... Parameters>
bool DecodeDetail(const std::vector<std::string>& rikDataVector, size_t& rnIndex, T1& rValue, Parameters&... args)
{

if(rikDataVector.size() - rnIndex - 1 != sizeof...(Parameters))
{
std::cout << "data src not equal cout of paramer" << std::endl;
return false;
}

if(!Convert(rikDataVector[rnIndex], rValue))
{
std::cout << "convert index = " << rnIndex << ", value = " << rikDataVector[rnIndex].c_str() << " to type = " << typeid(rValue).name() << " failed";
return false;
}

return DecodeDetail(rikDataVector, ++rnIndex, args...);
}

template<typename... Parameters>
bool Decode(const std::vector<std::string>& rikDataVector, Parameters&... args)
{

if(rikDataVector.empty() || sizeof...(Parameters) != rikDataVector.size())
{
std::cout << "data src not equal cout of paramer" << std::endl;
return false;
}

// 为了避免拷贝std::vector<std::string>,增加一个索引参数记录当前处理到的索引。或者用其他方式传指定索引开始的引用内容进去
size_t nIndex = 0;
return DecodeDetail(rikDataVector, nIndex, args...);
}



//////////////////////////////////////////////////////////////////////////
// 使用tuple的方式

#ifdef WIN32 // LINUX
template<typename ... Parameters>
std::tuple<Parameters ...> MakeTuple(Parameters ... args)
{
return std::make_tuple(args...);
}
#endif

template<typename... Parameters>
bool Decode(const std::vector<std::string>& rikDataVector, std::tuple<Parameters...>& riParamerTuple)
{

if(rikDataVector.empty() || sizeof...(Parameters) != rikDataVector.size())
{
std::cout << "data src not equal cout of paramer" << std::endl;
return false;
}

try
{
size_t nIndex = rikDataVector.size() - 1;
#ifndef WIN32 // CentOS7.0下GCC4.8测试正常,其他的没有测试。
riParamerTuple = std::make_tuple(boost::lexical_cast<Parameters>(rikDataVector[nIndex--])...);
#else
riParamerTuple = MakeTuple(boost::lexical_cast<Parameters>(rikDataVector[nIndex--])...);
#endif
// 这里要注意两点:
/* 1、为什么要用--运算符来逆序传入数据源?
考虑int i = 0; printf("%d,%d,%d", i++, i++, i++); 输出2,1,0 。因为默认的函数调用约定函数参数压栈顺序为从右向左,
所以为了确保传入的参数顺序是从第一个开始进行转换,就要使得传入的源数据顺序为 rikDataVector[0],rikDataVector[1],rikDataVector[3]...。
因此要用--。
*/

/* 2、为什么WIN32下要用函数MakeTuple来封装std::make_tuple?(以下测试均在VS2013中)。
因为实际测试中发现,直接使用std::make_tuple和boost::lexical_cast以及解变参模板运算符'...'的组合使用,会存在问题。
比如使用两个参数的测试数据时:
size_t nIndex = rikDataVector.size() - 1; // 这里测试数据是1
std::tuple<Parameters...> iTuple2((boost::lexical_cast<Parameters>(rikDataVector[nIndex--]))...);
// Parameters... = int, string 等效为 lexical_cast<string>(rikDataVector[0]), lexical_cast<int>(rikDataVector[1]) 会异常
// parameters... = string, int 等效为 lexical_cast<string>(rikDataVector[0]), lexical_cast<int>(rikDataVector[1])
// parameters... = int, float 等效为 lexical_cast<float>(rikDataVector[0]), lexical_cast<int>(rikDataVector[1]) 会异常
// parameters... = float, int 等效为 lexical_cast<float>(rikDataVector[0]), lexical_cast<int>(rikDataVector[1])
// parameters... = string, float 等效为 lexical_cast<string>(rikDataVector[0]), lexical_cast<float>(rikDataVector[1])
// parameters... = float, string 等效为 lexical_cast<string>(rikDataVector[0]), lexical_cast<float>(rikDataVector[1]) 会异常

没有去测试类型更多的结果,但是当我用如下Print函数类型推导输出全部参数时又没有问题。
// 打印变参模板形参值
template<typename T1>
void Print(T1&& rnValue)
{
std::cout << rnValue << std::endl;
}

template<typename T1, typename... Args>
void Print(T1&& rnValue, Args&&... args) // int, float, string
{
Print(rnValue);
Print(args...);
}

Print((boost::lexical_cast<Parameters>(rikDataVector[nIndex--]))...); // 没有问题。

综合比较:
Print((boost::lexical_cast<Parameters>(rikDataVector[nIndex--]))...);
std::tuple<Parameters...> iTuple2((boost::lexical_cast<Parameters>(rikDataVector[nIndex--]))...);

区别就是Print是我自定义的函数,tuple()是标准库函数。没有找到为什么会不同的原因。
但能想到也用一个函数去封装std::make_tuple,因此才有了MakeTuple函数。

// 附带MakeTuple测试结果:
// Parameters... = int, string 等效为 lexical_cast<int>(rikDataVector[0]), lexical_cast<string>(rikDataVector[1])
// parameters... = string, int 等效为 lexical_cast<string>(rikDataVector[0]), lexical_cast<int>(rikDataVector[1])
// parameters... = int, float 等效为 lexical_cast<int>(rikDataVector[0]), lexical_cast<float>(rikDataVector[1])
// parameters... = float, int 等效为 lexical_cast<float>(rikDataVector[0]), lexical_cast<int>(rikDataVector[1])
// parameters... = string, float 等效为 lexical_cast<string>(rikDataVector[0]), lexical_cast<float>(rikDataVector[1])
// parameters... = float, string 等效为 lexical_cast<float>(rikDataVector[0]), lexical_cast<string>(rikDataVector[1])


当然!还有另一种解决方法,就是用一个函数包装lexical_cast的转换,包装代码如下:
template<typename TypeDst, typename TypeSrc>
TypeDst Convert(const TypeSrc& rikSrc)
{
return boost::lexical_cast<TypeDst>(rikSrc);
}

*/


}
catch(const std::exception& rikException)
{
std::cout << "decode failed when user boost::lexical_cast and tuple" << std::endl;
return false;
}

return true;
}

// 注意:请自行确保rikData2DVector中每个一维数组中要解析的类型顺序是相同的。
template<typename... Parameters>
bool Decode(const std::vector<std::vector<std::string>>& rikData2DVector, std::vector<std::tuple<Parameters...>>& riParamerTupleVector)
{

for(size_t i = 0; i < rikData2DVector.size(); ++i)
{
std::tuple<Parameters...> iTmpTuple;
if(!Decode(rikData2DVector[i], iTmpTuple))
{
std::cout << "data src group[" << i << "] decode error!" << std::endl;
return false;
}

riParamerTupleVector.push_back(iTmpTuple);
}

return true;
}

int main()
{

int nInt = 0;
std::string iStr;
float fFloat = 0.0f;
bool bBool = false;

// 测试1: int, string
std::cout << "测试1: int, string ==> " << std::endl;
std::vector<std::string> iDataVector1{"1", "test1"};
if(false != Decode(iDataVector1, nInt, iStr))
{
std::cout << "bBool = " << bBool << ", nInt = " << nInt << ", fFloat = " << fFloat << ", iStr = " << iStr << std::endl;
}
else
{
std::cout << "test1 decode error" << std::endl;
}
std::cout << std::endl;

// 测试2: float, string , int
std::cout << "测试2: float, string , int ==> " << std::endl;
std::vector<std::string> iDataVector2{
"2.222", "test2", "2"};
if(false != Decode(iDataVector2, fFloat, iStr, nInt))
{
std::cout << "bBool = " << bBool << ", nInt = " << nInt << ", fFloat = " << fFloat << ", iStr = " << iStr << std::endl;
}
else
{
std::cout << "test2 decode error" << std::endl;
}
std::cout << std::endl;

// 测试3: string, bool, int, float
std::cout << "测试3: string, bool, int, float ==> " << std::endl;
std::vector<std::string> iDataVector3{"test3", "1", "3", "3.333"};
if(false != Decode(iDataVector3, iStr, bBool, nInt, fFloat))
{
std::cout << "bBool = " << bBool << ", nInt = " << nInt << ", fFloat = " << fFloat << ", iStr = " << iStr << std::endl;
}
else
{
std::cout << "test3 decode error" << std::endl;
}
std::cout << std::endl;


//////////////////////////////////////////////////////////////////////////
// 测试4: bool, int, float, string
std::cout << "测试4: bool, int, float, string ==> " << std::endl;
std::vector<std::string> iDataVector4{"0", "4", "4.444", "test4"};
std::tuple<bool, int, float, std::string> iParamerTuple4;
if(false != Decode(iDataVector4, iParamerTuple4))
{
std::cout << "tuple<0> = " << std::get<0>(iParamerTuple4) << ", tuple<1> = " << std::get<1>(iParamerTuple4)
<< ", tuple<2> = " << std::get<2>(iParamerTuple4) << ", tuple<3> = " << std::get<3>(iParamerTuple4) << std::endl;
}
else
{
std::cout << "test4 decode error" << std::endl;
}
std::cout << std::endl;

// 测试5: float, string, int,
std::cout << "测试5: float, string, int ==> " << std::endl;
std::vector<std::string> iDataVector5{"5.55", "test5", "555"};
std::tuple<float, std::string, int> iParamerTuple5;
if(false != Decode(iDataVector5, iParamerTuple5))
{
std::cout << "tuple<0> = " << std::get<0>(iParamerTuple5) << ", tuple<1> = " << std::get<1>(iParamerTuple5)
<< ", tuple<2> = " << std::get<2>(iParamerTuple5) << std::endl;
}
else
{
std::cout << "test5 decode error" << std::endl;
}
std::cout << std::endl;

// 测试6: string, int,
std::cout << "测试6: string, int ==> " << std::endl;
std::vector<std::string> iDataVector6{"test6", "66"};
std::tuple<std::string, int> iParamerTuple6;
if(false != Decode(iDataVector6, iParamerTuple6))
{
std::cout << "tuple<0> = " << std::get<0>(iParamerTuple6) << ", tuple<1> = " << std::get<1>(iParamerTuple6) << std::endl;
}
else
{
std::cout << "test6 decode error" << std::endl;
}
std::cout << std::endl;

// 测试7: int, string
std::cout << "测试7: int, string ==> " << std::endl;
std::vector<std::string> iDataVector7{"77", "test7"};
std::tuple<int, std::string> iParamerTuple7;
if(false != Decode(iDataVector7, iParamerTuple7))
{
std::cout << "tuple<0> = " << std::get<0>(iParamerTuple7) << ", tuple<1> = " << std::get<1>(iParamerTuple7) << std::endl;
}
else
{
std::cout << "test7 decode error" << std::endl;
}
std::cout << std::endl;

//////////////////////////////////////////////////////////////////////////
// 二维
// 测试8: {int, string},{int,string}{int, string}
std::cout << "测试8: {int, string},{int,string}{int, string} ==> " << std::endl;
std::vector<std::vector<std::string>> iData2DVector8{{"881", "test81"}, {"882", "test82"}};
std::vector<std::tuple<int, std::string>> iParamerTupleVector8;
if(false != Decode(iData2DVector8, iParamerTupleVector8))
{
for(const std::tuple<int, std::string>& rikParamerTuple : iParamerTupleVector8)
{
std::cout << "tuple<0> = " << std::get<0>(rikParamerTuple) << ", tuple<1> = " << std::get<1>(rikParamerTuple) << std::endl;
}
}
else
{
std::cout << "test8 decode error" << std::endl;
}
std::cout << std::endl;

char c;
std::cin >> c;
return 0;
}