C++学习------clocale头文件的源码学习02

127 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第22天,点击查看活动详情

续接上文,# C++学习------clocale头文件的源码学习01

函数定义

参考代码:www.aospxref.com/android-12.…

其实逻辑也比较清晰,主要是通过localeconv函数返回当前的配置结构体lconv,通过setlocale设置对应的locale。

99  struct lconv* localeconv(void) __INTRODUCED_IN_NO_GUARD_FOR_NDK(21);
100  
101  locale_t duplocale(locale_t __l) __INTRODUCED_IN(21);
102  void freelocale(locale_t __l) __INTRODUCED_IN(21);
103  locale_t newlocale(int __category_mask, const char* __locale_name, locale_t __base) __INTRODUCED_IN(21);
104  char* setlocale(int __category, const char* __locale_name);
105  locale_t uselocale(locale_t __l) __INTRODUCED_IN(21);
106  
107  #define LC_GLOBAL_LOCALE __BIONIC_CAST(reinterpret_cast, locale_t, -1L)
108  
109  __END_DECLS

接下来我们具体看看每个函数的具体实现:

localeconv---获取当前配置

注意到,这里使用了pthread_once,该线程的作用正如其名,在多线程环境下,__locale_init函数只会被执行一次,然后对全局静态变量g_locale进行了初始化。

130  lconv* localeconv() {
131    pthread_once(&g_locale_once, __locale_init);
132    return &g_locale;
133  }

许多符号的默认值都是空,小数点默认为'.',与位置有关的数值默认值都为CHAR_MAX(255);

85  static pthread_once_t g_locale_once = PTHREAD_ONCE_INIT;
86  static lconv g_locale;
87  
88  static void __locale_init() {
89    g_locale.decimal_point = const_cast<char*>(".");
90  
91    char* not_available = const_cast<char*>("");
92    g_locale.thousands_sep = not_available;
93    g_locale.grouping = not_available;
94    g_locale.int_curr_symbol = not_available;
95    g_locale.currency_symbol = not_available;
96    g_locale.mon_decimal_point = not_available;
97    g_locale.mon_thousands_sep = not_available;
98    g_locale.mon_grouping = not_available;
99    g_locale.positive_sign = not_available;
100    g_locale.negative_sign = not_available;
101  
102    g_locale.int_frac_digits = CHAR_MAX;
103    g_locale.frac_digits = CHAR_MAX;
104    g_locale.p_cs_precedes = CHAR_MAX;
105    g_locale.p_sep_by_space = CHAR_MAX;
106    g_locale.n_cs_precedes = CHAR_MAX;
107    g_locale.n_sep_by_space = CHAR_MAX;
108    g_locale.p_sign_posn = CHAR_MAX;
109    g_locale.n_sign_posn = CHAR_MAX;
110    g_locale.int_p_cs_precedes = CHAR_MAX;
111    g_locale.int_p_sep_by_space = CHAR_MAX;
112    g_locale.int_n_cs_precedes = CHAR_MAX;
113    g_locale.int_n_sep_by_space = CHAR_MAX;
114    g_locale.int_p_sign_posn = CHAR_MAX;
115    g_locale.int_n_sign_posn = CHAR_MAX;
116  }

duplocale---复制一个当前的配置结构体并返回

将传入的locale_t作为构造函数入参,new一个返回

135  locale_t duplocale(locale_t l) {
136    return new __locale_t(l);
137  }

对应locale_t被定义为__locale_t*

参考代码 www.aospxref.com/android-12.…

43  /* If we just use void* in the typedef, the compiler exposes that in error messages. */
44  struct __locale_t;
45  
46  /**
47   * The `locale_t` type that represents a locale.
48   */
49  typedef struct __locale_t* locale_t;

我们来看看__locale_t的定义,里面的特定构造函数实际上就是对这种传入locale_t的处理,如果该指针等于LC_GLOBAL_LOCALE,即reinterpret_cast<locale_t>(-1L),那么说明没有初始化,需要根据标志位进行初始化;由于这里面默认是使用utf8的locale配置,所以mb_cur_max就被设置为4,否则就是传入参数的值拷贝


53  static bool __bionic_current_locale_is_utf8 = true;

55  struct __locale_t {
56    size_t mb_cur_max;
57  
58    explicit __locale_t(size_t mb_cur_max) : mb_cur_max(mb_cur_max) {
59    }
60  
61    explicit __locale_t(const __locale_t* other) {
62      if (other == LC_GLOBAL_LOCALE) {
63        mb_cur_max = __bionic_current_locale_is_utf8 ? 4 : 1;
64      } else {
65        mb_cur_max = other->mb_cur_max;
66      }
67    }
68  
69    BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(__locale_t);
70  };

上面的BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(__locale_t);其实是禁止隐式构造的宏,如下: 将类的默认构造函数,拷贝构造函数,赋值重载都隐藏起来

#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
private:                     \
TypeName(const TypeName&);               \
TypeName& operator=(const TypeName&)

#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
private:                     \
TypeName();                                    \
DISALLOW_COPY_AND_ASSIGN(TypeName)

freelocale---释放传入的配置结构体

释放对应的指针即可

139  void freelocale(locale_t l) {
140    delete l;
141  }

newlocale---创建一个新的配置结构体

判断传入参数是否合法:是否是已知的mask,locale_name不可以为空;

143  locale_t newlocale(int category_mask, const char* locale_name, locale_t /*base*/) {
144    // Are 'category_mask' and 'locale_name' valid?
145    if ((category_mask & ~LC_ALL_MASK) != 0 || locale_name == nullptr) {
146      errno = EINVAL;
147      return nullptr;
148    }
149  

传入的locale_name是否支持

150    if (!__is_supported_locale(locale_name)) {
151      errno = ENOENT;
152      return nullptr;
153    }

通过函数__is_supported_locale判断,默认的字符串只能是如下几个:"","C","C.UTF-8","en_US.UTF-8","POSIX"

118  static bool __is_supported_locale(const char* locale_name) {
119    return (strcmp(locale_name, "") == 0 ||
120            strcmp(locale_name, "C") == 0 ||
121            strcmp(locale_name, "C.UTF-8") == 0 ||
122            strcmp(locale_name, "en_US.UTF-8") == 0 ||
123            strcmp(locale_name, "POSIX") == 0);
124  }

最后返回构造的__locale_t,这里事先决定其中的参数值,传入进行初始化

154  
155    return new __locale_t(__is_utf8_locale(locale_name) ? 4 : 1);
156  }

setlocale---设置当前配置

前面的检验流程与newlocale基本一致,判断是否合法;

158  char* setlocale(int category, const char* locale_name) {
159    // Is 'category' valid?
160    if (category < LC_CTYPE || category > LC_IDENTIFICATION) {
161      errno = EINVAL;
162      return nullptr;
163    }
164  
165    // Caller wants to set the locale rather than just query?
166    if (locale_name != nullptr) {
167      if (!__is_supported_locale(locale_name)) {
168        // We don't support this locale.
169        errno = ENOENT;
170        return nullptr;
171      }
172      __bionic_current_locale_is_utf8 = __is_utf8_locale(locale_name);
173    }
174  
175    return const_cast<char*>(__bionic_current_locale_is_utf8 ? "C.UTF-8" : "C");
176  }

然后给__bionic_current_locale_is_utf8赋值,通过locale_name判断,为空或者"UTF-8"置为true; 返回对应的字符串

126  static bool __is_utf8_locale(const char* locale_name) {
127    return (*locale_name == '\0' || strstr(locale_name, "UTF-8"));
128  }

uselocale---使用当前传入的配置

首先通过get_current_locale_ptr获取当前的locale,如果当前为空,说明是第一次调用,那么默认使用LC_GLOBAL_LOCALE给old_locale赋值; 如果新的不为空,则对应赋值即可,返回old_locale;

186  locale_t uselocale(locale_t new_locale) {
187    locale_t old_locale = *get_current_locale_ptr();
188  
189    // If this is the first call to uselocale(3) on this thread, we return LC_GLOBAL_LOCALE.
190    if (old_locale == nullptr) {
191      old_locale = LC_GLOBAL_LOCALE;
192    }
193  
194    if (new_locale != nullptr) {
195      *get_current_locale_ptr() = new_locale;
196    }
197  
198    return old_locale;
199  }

其中get_current_locale_ptr的逻辑如下:

178  static locale_t* get_current_locale_ptr() {
179  #if USE_TLS_SLOT
180    return &__get_bionic_tls().locale;
181  #else
182    return &g_current_locale;
183  #endif
184  }

分两种情况,如果没有定义USE_TLS_SLOT,那么直接返回g_current_locale的地址即可,后面其它模块也会使用这个全局变量的值做相关的判断

81  #if !USE_TLS_SLOT
82  static thread_local locale_t g_current_locale;
83  #endif

如果定义了USE_TLS_SLOT,那么要通过__get_bionic_tls获取线程局部存储里面的locale信息,具体__get_tls函数的执行与过程分析可以参考# C++学习------cerrno头文件的作用与源码学习里面关于__get_tls函数的调用过程与原理。

参考链接:__get_bionic_tls

202  static inline __always_inline bionic_tls& __get_bionic_tls() {
203    return *static_cast<bionic_tls*>(__get_tls()[TLS_SLOT_BIONIC_TLS]);
204  }

LC_GLOBAL_LOCALE

这个宏实际上是reinterpret_cast<locale_t>(-1L)的意思,实际上就是使用-1L初始化locale_t。

57  #if defined(__cplusplus)
58  #define __BIONIC_CAST(_k,_t,_v) (_k<_t>(_v))
59  #else
60  #define __BIONIC_CAST(_k,_t,_v) ((_t) (_v))
61  #endif