函数定义
参考代码: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