【转】tars源码漫谈第1篇------tc_loki.h (牛逼哄哄的loki库)
程序员文章站
2022-06-30 13:32:05
loki库是C++模板大牛Andrei写的, 里面大量运用模板的特性, 而tc_loki.h借用了loki库的部分代码, 形成了一个基本的文件tc_loki.h, 来看看: 是不是有一种看天书的感觉, 确实如此。 也行, 姑且把它当成和STL类似的基础库, 别纠结于它。 转自:https://blo ......
loki库是c++模板大牛andrei写的, 里面大量运用模板的特性, 而tc_loki.h借用了loki库的部分代码, 形成了一个基本的文件tc_loki.h, 来看看:
1 #ifndef __tc_typetraits_h 2 #define __tc_typetraits_h 3 4 #include <memory> 5 6 namespace tars 7 { 8 ///////////////////////////////////////////////// 9 // 说明: loki 10 ///////////////////////////////////////////////// 11 12 namespace tl 13 { 14 //只声明, 不定义的类, 作为typelist的末端类型 15 class nulltype; 16 17 //空类型 18 struct emptytype { }; 19 20 /** 21 * 数值到类型的映射 22 */ 23 template<int v> 24 struct int2type 25 { 26 enum { value = v }; 27 }; 28 29 /** 30 * 类型到类型的映射 31 */ 32 template<typename t> 33 struct type2type 34 { 35 typedef t originaltype; 36 }; 37 38 /////////////////////////////////////////////////////////////////////////// 39 // 以下是typelist的定义(目前只支持10个参数) 40 /** 41 * 定义类型链表 42 */ 43 template<typename head, typename tail> 44 struct typelist 45 { 46 typedef head h; 47 typedef tail t; 48 }; 49 50 #define typelist_1(t1) typelist<t1, tl::nulltype> 51 #define typelist_2(t1, t2) typelist<t1, tl::typelist_1(t2)> 52 #define typelist_3(t1, t2, t3) typelist<t1, tl::typelist_2(t2, t3)> 53 #define typelist_4(t1, t2, t3, t4) typelist<t1, tl::typelist_3(t2, t3, t4)> 54 #define typelist_5(t1, t2, t3, t4, t5) typelist<t1, tl::typelist_4(t2, t3, t4, t5)> 55 #define typelist_6(t1, t2, t3, t4, t5, t6) typelist<t1, tl::typelist_5(t2, t3, t4, t5, t6)> 56 #define typelist_7(t1, t2, t3, t4, t5, t6, t7) typelist<t1, tl::typelist_6(t2, t3, t4, t5, t6, t7)> 57 #define typelist_8(t1, t2, t3, t4, t5, t6, t7, t8) typelist<t1, tl::typelist_7(t2, t3, t4, t5, t6, t7, t8)> 58 #define typelist_9(t1, t2, t3, t4, t5, t6, t7, t8, t9) typelist<t1, tl::typelist_8(t2, t3, t4, t5, t6, t7, t8, t9)> 59 #define typelist_10(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) typelist<t1, tl::typelist_9(t2, t3, t4, t5, t6, t7, t8, t9, t10)> 60 #define typelist_11(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) typelist<t1, tl::typelist_10(t2, t3, t4, t5, t6, t7, t8, t9, t10, t11)> 61 #define typelist_12(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) typelist<t1, tl::typelist_11(t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12)> 62 #define typelist_13(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) typelist<t1, tl::typelist_12(t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13)> 63 #define typelist_14(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) typelist<t1, tl::typelist_13(t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14)> 64 #define typelist_15(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15) typelist<t1, tl::typelist_14(t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15)> 65 #define typelist_16(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16) typelist<t1, tl::typelist_15(t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16)> 66 #define typelist_17(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17) typelist<t1, tl::typelist_16(t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17)> 67 #define typelist_18(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18) typelist<t1, tl::typelist_17(t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18)> 68 #define typelist_19(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19) typelist<t1, tl::typelist_18(t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19)> 69 #define typelist_20(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20) typelist<t1, tl::typelist_19(t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20)> 70 71 72 ////////////////////////////////////////////////////////////////////////////// 73 // 以下定义typelist的编译期的操作函数(通过偏特化实现) 74 /** 75 * length: 取typelist的长度 76 */ 77 template<class tlist> struct length; 78 template<> struct length<nulltype> 79 { 80 enum { value = 0 }; 81 }; 82 template<class head, class tail> struct length<typelist<head, tail> > 83 { 84 enum { value = 1 + length<tail>::value }; 85 }; 86 87 /** 88 * typeat, 取链表在i位置上的类型 89 */ 90 template<class tlist, unsigned int i> struct typeat; 91 template<class head, class tail> struct typeat<typelist<head, tail>, 0> 92 { 93 typedef head result; 94 }; 95 template<class head, class tail, unsigned int i> struct typeat<typelist<head, tail>, i> 96 { 97 typedef typename typeat<tail, i-1>::result result; 98 }; 99 100 /** 101 * typeat, 取链表在i位置上的类型, i超出了返回, 则返回defaulttype 102 */ 103 template<class tlist, unsigned int index, typename defaulttype = nulltype> struct typeatnonstrict 104 { 105 typedef defaulttype result; 106 }; 107 template <class head, class tail, typename defaulttype> struct typeatnonstrict<typelist<head, tail>, 0, defaulttype> 108 { 109 typedef head result; 110 }; 111 template <class head, class tail, unsigned int i, typename defaulttype> struct typeatnonstrict<typelist<head, tail>, i, defaulttype> 112 { 113 typedef typename typeatnonstrict<tail, i - 1, defaulttype>::result result; 114 }; 115 116 /** 117 * 取链表上类型为t的序号, 没有则返回-1 118 */ 119 template<class tlist, class t> struct indexof; 120 template<class t> struct indexof<nulltype, t> 121 { 122 enum { value = -1 }; 123 }; 124 template<class tail, class t> struct indexof<typelist<t,tail>, t> 125 { 126 enum { value = 0 }; 127 }; 128 template<class head, class tail, class t> struct indexof<typelist<head, tail>, t> 129 { 130 private: 131 enum { temp = indexof<tail, t>::value }; 132 public: 133 enum { value = temp == -1 ? -1 : 1 + temp }; 134 }; 135 136 /** 137 * append, 添加到链表尾部 138 */ 139 template<class tlist, class t> struct append; 140 template<> struct append<nulltype, nulltype> 141 { 142 typedef nulltype result; 143 }; 144 template<class t> struct append<nulltype,t> 145 { 146 typedef typelist_1(t) result; 147 }; 148 template<class head, class tail> struct append<nulltype, typelist<head, tail> > 149 { 150 typedef typelist<head, tail> result; 151 }; 152 template<class head, class tail, class t> struct append<typelist<head, tail>, t> 153 { 154 typedef typelist<head, typename append<tail, t>::result> result; 155 }; 156 157 /** 158 * erase 删除 159 */ 160 template<class tlist, class t> struct erase; 161 template<class t> struct erase<nulltype, t> 162 { 163 typedef nulltype result; 164 }; 165 template<class t, class tail> struct erase<typelist<t, tail>, t> 166 { 167 typedef tail result; 168 }; 169 template<class head, class tail, class t> struct erase<typelist<head, tail>, t> 170 { 171 typedef typelist<head, typename erase<tail, t>::result> result; 172 }; 173 174 /** 175 * eraseall 删除 176 */ 177 template<class tlist, class t> struct eraseall; 178 template<class t> struct eraseall<nulltype, t> 179 { 180 typedef nulltype result; 181 }; 182 template<class t, class tail> struct eraseall<typelist<t, tail>, t> 183 { 184 typedef typename eraseall<tail, t>::result result; 185 }; 186 template<class head, class tail, class t> struct eraseall<typelist<head, tail>, t> 187 { 188 typedef typelist<head, typename eraseall<tail, t>::result> result; 189 }; 190 191 /** 192 * 生成typelist类型 193 */ 194 template<class t1=nulltype, class t2=nulltype, class t3=nulltype, class t4=nulltype, class t5=nulltype, 195 class t6=nulltype, class t7=nulltype, class t8=nulltype, class t9=nulltype, class t10=nulltype, 196 class t11=nulltype, class t12=nulltype, class t13=nulltype, class t14=nulltype, class t15=nulltype, 197 class t16=nulltype, class t17=nulltype, class t18=nulltype, class t19=nulltype, class t20=nulltype> 198 struct tlmaker 199 { 200 private: 201 typedef typelist_20(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, 202 t11, t12, t13, t14, t15, t16, t17, t18, t19, t20) tmplist; 203 public: 204 typedef typename eraseall<tmplist, nulltype>::result result; 205 }; 206 207 ///////////////////////////////////////////////////////////////////////////////////// 208 //判断类型t是否可以转换成类型u(参考了wbl库, 直接采用loki, 编译时会有警告 209 //关键:如果能够转换, 则接收u的函数, 也能够接收t 210 template<class t, class u> 211 class conversion 212 { 213 protected: 214 typedef char yes; 215 struct no {char dummy[2];}; 216 struct any_conversion 217 { 218 template <typename p> any_conversion(const volatile p&); 219 template <typename p> any_conversion(p&); 220 }; 221 222 template <typename p> struct conversion_checker 223 { 224 static no _m_check(any_conversion ...); 225 static yes _m_check(p, int); 226 }; 227 228 static t _m_from; 229 public: 230 enum 231 { 232 //是否可以转换(如果test(maket())匹配到了static small test(u), 则可以转换) 233 exists = (sizeof(conversion_checker<u>::_m_check(_m_from, 0)) == sizeof(yes)), 234 //是否可以双向转换 235 exists2way = exists && conversion<u, t>::exists, 236 //是否相同类型 237 sametype = false 238 }; 239 }; 240 241 //偏特化来确定sametype 242 template<class t> 243 class conversion<t, t> 244 { 245 public: 246 enum 247 { 248 exists = true, 249 exists2way = true, 250 sametype = true 251 }; 252 }; 253 254 //判断两个类是否可以继承 255 //关键:子类指针可以转换成父类指针, 且不是void*类型 256 //相同类型, supersubclass判断为true 257 #define supersubclass(t, u) (tl::conversion<const u*, const t*>::exists && !tl::conversion<const t*, const void*>::sametype) 258 //相同类型, supersubclass_strict判断为false 259 #define supersubclass_strict(t, u) (supersubclass(t, u) && !tl::conversion<const t, const u>::sametype) 260 261 /////////////////////////////////////////////////////////////////////////////////////////////// 262 // 类型选择器 263 template<bool flag, typename u, typename v> 264 struct typeselect 265 { 266 typedef u result; 267 }; 268 269 template<typename u, typename v> 270 struct typeselect<false, u, v> 271 { 272 typedef v result; 273 }; 274 275 /////////////////////////////////////////////////////////////////////////////////////// 276 /** 277 * 类型萃取器, copy至loki库 278 */ 279 template<typename t> 280 class typetraits 281 { 282 private: 283 284 /////////////////////////////////////////////////////// 285 //提取引用的原始类型(即去掉引用类型) 286 template<class u> 287 struct referencetraits 288 { 289 enum { result = false }; 290 typedef u result; 291 }; 292 293 template<class u> 294 struct referencetraits<u&> 295 { 296 enum { result = true }; 297 typedef u result; 298 }; 299 300 /////////////////////////////////////////////////////// 301 //指针类型 302 template<class u> 303 struct pointertraits 304 { 305 enum { result = false }; 306 typedef tl::nulltype result; 307 }; 308 309 template<class u> 310 struct pointertraits<u*> 311 { 312 enum { result = true }; 313 typedef u result; 314 }; 315 316 template<class u> 317 struct pointertraits<u*&> 318 { 319 enum { result = true }; 320 typedef u result; 321 }; 322 323 /////////////////////////////////////////////////////// 324 //成员函数指针, gcc下面支持有问题, 屏蔽之 325 template<typename u> 326 struct pointertomembertraits 327 { 328 enum { result = false }; 329 }; 330 331 template<class u, class v> 332 struct pointertomembertraits<u v::*> 333 { 334 enum { result = true }; 335 }; 336 337 template<class u, class v> 338 struct pointertomembertraits<u v::*&> 339 { 340 enum { result = true }; 341 }; 342 343 /////////////////////////////////////////////////////// 344 // const 345 template<typename u> 346 struct unconsttraits 347 { 348 enum { result = false }; 349 typedef u result; 350 }; 351 template<typename u> 352 struct unconsttraits<const u> 353 { 354 enum { result = true }; 355 typedef u result; 356 }; 357 template<typename u> 358 struct unconsttraits<const u&> 359 { 360 enum { result = true }; 361 typedef u& result; 362 }; 363 364 /////////////////////////////////////////////////////// 365 // volatile 366 template<typename u> 367 struct unvolatiletraits 368 { 369 enum { result = false }; 370 typedef u result; 371 }; 372 template<typename u> 373 struct unvolatiletraits<volatile u> 374 { 375 enum { result = true }; 376 typedef u result; 377 }; 378 template<typename u> 379 struct unvolatiletraits<volatile u&> 380 { 381 enum { result = true }; 382 typedef u& result; 383 }; 384 public: 385 //t是否是指针类型 386 enum { ispointer = pointertraits<t>::result }; 387 //t是否是引用类型 388 enum { isreference = referencetraits<t>::result }; 389 //t是否指向成员函数的指针 390 enum { ismemberpointer = pointertomembertraits<t>::result }; 391 392 //t是否是const类型 393 enum { isconst = unconsttraits<t>::result }; 394 //t是否是volatile类型 395 enum { isvolatile = unvolatiletraits<t>::result }; 396 397 //如果t是指针类型,则获取t的原类型, 即去掉指针类型 398 typedef typename pointertraits<t>::result pointeetype; 399 //如果t是引用类型,则获取t的原类型, 即去掉引用类型 400 typedef typename referencetraits<t>::result referencedtype; 401 //如果t是const类型,则获取t的原类型, 即去掉const类型 402 typedef typename unconsttraits<t>::result nonconsttype; 403 //如果t是volatile类型,则获取t的原类型, 即去掉volatile类型 404 typedef typename unvolatiletraits<t>::result nonvolatiletype; 405 //去掉const volatile类型 406 typedef typename unvolatiletraits<typename unconsttraits<t>::result>::result unqualifiedtype; 407 408 public: 409 410 ////////////////////////////////////////////////////// 411 // 412 typedef tl::tlmaker<unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long>::result unsignedints; 413 typedef tl::tlmaker<signed char, short, int, long, long long>::result signedints; 414 typedef tl::tlmaker<bool, char, wchar_t>::result otherints; 415 typedef tl::tlmaker<float, double, long double>::result floats; 416 typedef tl::typelist_2(tl::emptytype, tl::nulltype) nulltypes; 417 418 //无符号整形 419 enum { isstdunsignedint = tl::indexof<unsignedints, t>::value >= 0 }; 420 //有符号整形 421 enum { isstdsignedint = tl::indexof<signedints, t>::value >= 0 }; 422 //整形 423 enum { isstdint = isstdunsignedint || isstdsignedint || tl::indexof<otherints, t>::value >= 0 }; 424 //浮点类型 425 enum { isstdfloat = tl::indexof<floats, t>::value >= 0 }; 426 //数值类型 427 enum { isstdarith = isstdint || isstdfloat }; 428 //基础类型(包括void) 429 enum { isstdfundamental = isstdarith || tl::indexof<tl::typelist_1(void), t>::value >= 0}; 430 //空类型 431 enum { isnulltype = tl::indexof<nulltypes, t>::value >= 0 }; 432 //简单类型 433 enum { isbasetype = isstdarith || ispointer || ismemberpointer }; 434 435 //对于复杂类型, 获取数据的引用类型, 即加上引用类型 436 typedef typename typeselect<isbasetype, t, referencedtype&>::result referencetype; 437 438 //对于复杂类型且非空类型, 获取数据的引用类型, 即加上引用类型 439 //typedef typename typeselect<isbasetype || isnulltype, t, referencedtype&>::result referencetypeex; 440 441 //获取数据的原类型, 消除引用的引用这种情况 442 typedef typename typeselect<!isreference, t, referencedtype&>::result parametertype; 443 }; 444 445 //////////////////////////////////////////////////////////////////////////////////////////////////// 446 //下面的使用开始展示typelist的威力, 用于自动生成class 447 448 //散乱的继承体系 449 template<class tlist, template <class> class unit> 450 class scatterhierarchy; 451 452 /* 453 namespace p 454 { 455 //注释copy至loki库 456 // the following type helps to overcome subtle flaw in the original 457 // implementation of genscatterhierarchy. 458 // the flaw is revealed when the input type list of genscatterhierarchy 459 // contains more then tars element of the same type (e.g. loki_typelist_2(int, int)). 460 // in this case genscatterhierarchy will contain multiple bases of the same 461 // type and some of them will not be reachable (per 10.3). 462 // for example before the fix the first element of tuple<loki_typelist_2(int, int)> 463 // is not reachable in any way! 464 template<class, class> 465 struct scatterhierarchytag; 466 } 467 template<class t1, class t2, template <class> class unit> 468 class scatterhierarchy<typelist<t1, t2>, unit> : public scatterhierarchy<p::scatterhierarchytag<t1, t2>, unit> 469 , public scatterhierarchy<t2, unit> 470 { 471 public: 472 typedef typelist<t1, t2> tlist; 473 typedef scatterhierarchy<p::scatterhierarchytag<t1, t2>, unit> leftbase; 474 typedef scatterhierarchy<t2, unit> rightbase; 475 template<typename t> struct rebind 476 { 477 typedef unit<t> result; 478 }; 479 }; 480 // in the middle *unique* class that resolve possible ambiguity 481 template <class t1, class t2, template <class> class unit> 482 class scatterhierarchy<p::scatterhierarchytag<t1, t2>, unit> 483 : public scatterhierarchy<t1, unit> 484 { 485 }; 486 */ 487 488 //具现化继承体系 489 template <class t1, class t2, template <class> class unit> 490 class scatterhierarchy<typelist<t1, t2>, unit> 491 : public scatterhierarchy<t1, unit> 492 , public scatterhierarchy<t2, unit> 493 { 494 public: 495 typedef typelist<t1, t2> tlist; 496 typedef scatterhierarchy<t1, unit> leftbase; 497 typedef scatterhierarchy<t2, unit> rightbase; 498 template <typename t> struct rebind 499 { 500 typedef unit<t> result; 501 }; 502 }; 503 504 template<class atomictype, template <class> class unit> 505 class scatterhierarchy : public unit<atomictype> 506 { 507 public: 508 typedef unit<atomictype> leftbase; 509 510 template<typename t> struct rebind 511 { 512 typedef unit<t> result; 513 }; 514 }; 515 516 template<template <class> class unit> 517 class scatterhierarchy<nulltype, unit> 518 { 519 public: 520 template<typename t> struct rebind 521 { 522 typedef unit<t> result; 523 }; 524 }; 525 526 ///////////////////////////////////////////////////////////////////// 527 //构建继承体系后, 采用下面的函数获取继承体系中某个类 528 529 template<class t, class h> 530 struct fieldhelperbytype 531 { 532 typedef typename h::template rebind<t>::result resulttype; 533 static resulttype& dochange(h &obj) 534 { 535 return static_cast<resulttype&>(obj); 536 } 537 }; 538 template<class t, class h> 539 struct fieldhelperbytype<t, const h> 540 { 541 typedef const typename h::template rebind<t>::result resulttype; 542 static resulttype& dochange(const h &obj) 543 { 544 return (resulttype&)obj; 545 } 546 }; 547 548 //直接按照类型获取, 如果有两个相同的类型, 则编译不过 549 template<class t, class h> 550 typename fieldhelperbytype<t, h>::resulttype& field(h &obj) 551 { 552 return fieldhelperbytype<t, h>::dochange(obj); 553 } 554 555 ///////////////////////////////////////////////////////////////////// 556 // 根据索引获取字段 557 //定义tuple, 默认的数据操作器(unit) 558 template<typename t> 559 struct tupleunit 560 { 561 t _value; 562 operator t&() { return _value; } 563 operator const t&() const { return _value; } 564 }; 565 566 template<class tlist> 567 struct tuple : public scatterhierarchy<tlist, tupleunit> 568 { 569 }; 570 571 //定义fieldhelperbyindex 572 template<class h, unsigned int i> struct fieldhelperbyindex; 573 574 //特化版本的fieldhelperbyindex, 推导出最后一个元素 575 template<class h> 576 struct fieldhelperbyindex<h, 0> 577 { 578 typedef typename h::tlist::h elementtype; 579 typedef typename h::template rebind<elementtype>::result unittype; 580 581 enum 582 { 583 istuple = conversion<unittype, tupleunit<elementtype> >::sametype, 584 isconst = typetraits<h>::isconst 585 }; 586 587 typedef const typename h::leftbase constleftbase; 588 typedef typename typeselect<isconst, constleftbase, typename h::leftbase>::result leftbase; 589 typedef typename typeselect<istuple, elementtype, unittype>::result unqualifiedresulttype; 590 typedef typename typeselect<isconst, const unqualifiedresulttype, unqualifiedresulttype>::result resulttype; 591 592 static resulttype &dochange(h& obj) 593 { 594 leftbase &leftbase = obj; 595 return (resulttype&)leftbase; 596 } 597 }; 598 599 //根据索引获取fieldhelper 600 template<class h, unsigned int i> 601 struct fieldhelperbyindex 602 { 603 typedef typename typeat<typename h::tlist, i>::result elementtype; 604 typedef typename h::template rebind<elementtype>::result unittype; 605 606 enum 607 { 608 istuple = conversion<unittype, tupleunit<elementtype> >::sametype, 609 isconst = typetraits<h>::isconst 610 }; 611 612 typedef const typename h::rightbase constrightbase; 613 typedef typename typeselect<isconst, constrightbase, typename h::rightbase>::result rightbase; 614 typedef typename typeselect<istuple, elementtype, unittype>::result unqualifiedresulttype; 615 typedef typename typeselect<isconst, const unqualifiedresulttype, unqualifiedresulttype>::result resulttype; 616 617 static resulttype &dochange(h& obj) 618 { 619 rightbase &rightbase = obj; 620 return fieldhelperbyindex<rightbase, i-1>::dochange(rightbase); 621 } 622 }; 623 624 //定义按照索引获取 625 template<unsigned int i, class h> 626 typename fieldhelperbyindex<h, i>::resulttype &field(h& obj) 627 { 628 return fieldhelperbyindex<h, i>::dochange(obj); 629 } 630 } 631 632 } 633 #endif 634
是不是有一种看天书的感觉, 确实如此。
也行, 姑且把它当成和stl类似的基础库, 别纠结于它。
转自:https://blog.csdn.net/stpeace/article/details/79772562
上一篇: 我对程序员的认识
下一篇: 我不相信会有一见钟情