/src/glib/gobject/gvalue.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* GObject - GLib Type, Object, Parameter and Signal Library |
2 | | * Copyright (C) 1997-1999, 2000-2001 Tim Janik and Red Hat, Inc. |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | * |
6 | | * This library is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * This library is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General |
17 | | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | /* |
21 | | * FIXME: MT-safety |
22 | | */ |
23 | | |
24 | | #include "config.h" |
25 | | |
26 | | #include <string.h> |
27 | | |
28 | | #include "gvalue.h" |
29 | | #include "gvaluecollector.h" |
30 | | #include "gbsearcharray.h" |
31 | | #include "gtype-private.h" |
32 | | |
33 | | |
34 | | /** |
35 | | * SECTION:generic_values |
36 | | * @short_description: A polymorphic type that can hold values of any |
37 | | * other type |
38 | | * @see_also: The fundamental types which all support #GValue |
39 | | * operations and thus can be used as a type initializer for |
40 | | * g_value_init() are defined by a separate interface. See the |
41 | | * [standard values API][gobject-Standard-Parameter-and-Value-Types] |
42 | | * for details |
43 | | * @title: Generic values |
44 | | * |
45 | | * The #GValue structure is basically a variable container that consists |
46 | | * of a type identifier and a specific value of that type. |
47 | | * |
48 | | * The type identifier within a #GValue structure always determines the |
49 | | * type of the associated value. |
50 | | * |
51 | | * To create an undefined #GValue structure, simply create a zero-filled |
52 | | * #GValue structure. To initialize the #GValue, use the g_value_init() |
53 | | * function. A #GValue cannot be used until it is initialized. Before |
54 | | * destruction you must always use g_value_unset() to make sure allocated |
55 | | * memory is freed. |
56 | | * |
57 | | * The basic type operations (such as freeing and copying) are determined |
58 | | * by the #GTypeValueTable associated with the type ID stored in the #GValue. |
59 | | * Other #GValue operations (such as converting values between types) are |
60 | | * provided by this interface. |
61 | | * |
62 | | * The code in the example program below demonstrates #GValue's |
63 | | * features. |
64 | | * |
65 | | * |[<!-- language="C" --> |
66 | | * #include <glib-object.h> |
67 | | * |
68 | | * static void |
69 | | * int2string (const GValue *src_value, |
70 | | * GValue *dest_value) |
71 | | * { |
72 | | * if (g_value_get_int (src_value) == 42) |
73 | | * g_value_set_static_string (dest_value, "An important number"); |
74 | | * else |
75 | | * g_value_set_static_string (dest_value, "What's that?"); |
76 | | * } |
77 | | * |
78 | | * int |
79 | | * main (int argc, |
80 | | * char *argv[]) |
81 | | * { |
82 | | * // GValues must be initialized |
83 | | * GValue a = G_VALUE_INIT; |
84 | | * GValue b = G_VALUE_INIT; |
85 | | * const gchar *message; |
86 | | * |
87 | | * // The GValue starts empty |
88 | | * g_assert (!G_VALUE_HOLDS_STRING (&a)); |
89 | | * |
90 | | * // Put a string in it |
91 | | * g_value_init (&a, G_TYPE_STRING); |
92 | | * g_assert (G_VALUE_HOLDS_STRING (&a)); |
93 | | * g_value_set_static_string (&a, "Hello, world!"); |
94 | | * g_printf ("%s\n", g_value_get_string (&a)); |
95 | | * |
96 | | * // Reset it to its pristine state |
97 | | * g_value_unset (&a); |
98 | | * |
99 | | * // It can then be reused for another type |
100 | | * g_value_init (&a, G_TYPE_INT); |
101 | | * g_value_set_int (&a, 42); |
102 | | * |
103 | | * // Attempt to transform it into a GValue of type STRING |
104 | | * g_value_init (&b, G_TYPE_STRING); |
105 | | * |
106 | | * // An INT is transformable to a STRING |
107 | | * g_assert (g_value_type_transformable (G_TYPE_INT, G_TYPE_STRING)); |
108 | | * |
109 | | * g_value_transform (&a, &b); |
110 | | * g_printf ("%s\n", g_value_get_string (&b)); |
111 | | * |
112 | | * // Attempt to transform it again using a custom transform function |
113 | | * g_value_register_transform_func (G_TYPE_INT, G_TYPE_STRING, int2string); |
114 | | * g_value_transform (&a, &b); |
115 | | * g_printf ("%s\n", g_value_get_string (&b)); |
116 | | * return 0; |
117 | | * } |
118 | | * ]| |
119 | | * |
120 | | * See also [gobject-Standard-Parameter-and-Value-Types] for more information on |
121 | | * validation of #GValue. |
122 | | * |
123 | | * For letting a #GValue own (and memory manage) arbitrary types or pointers, |
124 | | * they need to become a [boxed type][gboxed]. The example below shows how |
125 | | * the pointer `mystruct` of type `MyStruct` is used as a [boxed type][gboxed]. |
126 | | * |
127 | | * |[<!-- language="C" --> |
128 | | * typedef struct { ... } MyStruct; |
129 | | * G_DEFINE_BOXED_TYPE (MyStruct, my_struct, my_struct_copy, my_struct_free) |
130 | | * |
131 | | * // These two lines normally go in a public header. By GObject convention, |
132 | | * // the naming scheme is NAMESPACE_TYPE_NAME: |
133 | | * #define MY_TYPE_STRUCT (my_struct_get_type ()) |
134 | | * GType my_struct_get_type (void); |
135 | | * |
136 | | * void |
137 | | * foo () |
138 | | * { |
139 | | * GValue *value = g_new0 (GValue, 1); |
140 | | * g_value_init (value, MY_TYPE_STRUCT); |
141 | | * g_value_set_boxed (value, mystruct); |
142 | | * // [... your code ....] |
143 | | * g_value_unset (value); |
144 | | * g_free (value); |
145 | | * } |
146 | | * ]| |
147 | | */ |
148 | | |
149 | | |
150 | | /* --- typedefs & structures --- */ |
151 | | typedef struct { |
152 | | GType src_type; |
153 | | GType dest_type; |
154 | | GValueTransform func; |
155 | | } TransformEntry; |
156 | | |
157 | | |
158 | | /* --- prototypes --- */ |
159 | | static gint transform_entries_cmp (gconstpointer bsearch_node1, |
160 | | gconstpointer bsearch_node2); |
161 | | |
162 | | |
163 | | /* --- variables --- */ |
164 | | static GBSearchArray *transform_array = NULL; |
165 | | static GBSearchConfig transform_bconfig = { |
166 | | sizeof (TransformEntry), |
167 | | transform_entries_cmp, |
168 | | G_BSEARCH_ARRAY_ALIGN_POWER2, |
169 | | }; |
170 | | |
171 | | |
172 | | /* --- functions --- */ |
173 | | void |
174 | | _g_value_c_init (void) |
175 | 4 | { |
176 | 4 | transform_array = g_bsearch_array_create (&transform_bconfig); |
177 | 4 | } |
178 | | |
179 | | static inline void /* keep this function in sync with gvaluecollector.h and gboxed.c */ |
180 | | value_meminit (GValue *value, |
181 | | GType value_type) |
182 | 3.38k | { |
183 | 3.38k | value->g_type = value_type; |
184 | 3.38k | memset (value->data, 0, sizeof (value->data)); |
185 | 3.38k | } |
186 | | |
187 | | /** |
188 | | * g_value_init: |
189 | | * @value: A zero-filled (uninitialized) #GValue structure. |
190 | | * @g_type: Type the #GValue should hold values of. |
191 | | * |
192 | | * Initializes @value with the default value of @type. |
193 | | * |
194 | | * Returns: (transfer none): the #GValue structure that has been passed in |
195 | | */ |
196 | | GValue* |
197 | | g_value_init (GValue *value, |
198 | | GType g_type) |
199 | 1.69k | { |
200 | 1.69k | GTypeValueTable *value_table; |
201 | | /* g_return_val_if_fail (G_TYPE_IS_VALUE (g_type), NULL); be more elaborate below */ |
202 | 1.69k | g_return_val_if_fail (value != NULL, NULL); |
203 | | /* g_return_val_if_fail (G_VALUE_TYPE (value) == 0, NULL); be more elaborate below */ |
204 | | |
205 | 1.69k | value_table = g_type_value_table_peek (g_type); |
206 | | |
207 | 1.69k | if (value_table && G_VALUE_TYPE (value) == 0) |
208 | 1.69k | { |
209 | | /* setup and init */ |
210 | 1.69k | value_meminit (value, g_type); |
211 | 1.69k | value_table->value_init (value); |
212 | 1.69k | } |
213 | 0 | else if (G_VALUE_TYPE (value)) |
214 | 0 | g_critical ("%s: cannot initialize GValue with type '%s', the value has already been initialized as '%s'", |
215 | 0 | G_STRLOC, |
216 | 0 | g_type_name (g_type), |
217 | 0 | g_type_name (G_VALUE_TYPE (value))); |
218 | 0 | else /* !G_TYPE_IS_VALUE (g_type) */ |
219 | 0 | g_critical ("%s: cannot initialize GValue with type '%s', %s", |
220 | 1.69k | G_STRLOC, |
221 | 1.69k | g_type_name (g_type), |
222 | 1.69k | value_table ? "this type is abstract with regards to GValue use, use a more specific (derived) type" : "this type has no GTypeValueTable implementation"); |
223 | 1.69k | return value; |
224 | 1.69k | } |
225 | | |
226 | | /** |
227 | | * g_value_copy: |
228 | | * @src_value: An initialized #GValue structure. |
229 | | * @dest_value: An initialized #GValue structure of the same type as @src_value. |
230 | | * |
231 | | * Copies the value of @src_value into @dest_value. |
232 | | */ |
233 | | void |
234 | | g_value_copy (const GValue *src_value, |
235 | | GValue *dest_value) |
236 | 1.69k | { |
237 | 1.69k | g_return_if_fail (src_value); |
238 | 1.69k | g_return_if_fail (dest_value); |
239 | 1.69k | g_return_if_fail (g_value_type_compatible (G_VALUE_TYPE (src_value), G_VALUE_TYPE (dest_value))); |
240 | | |
241 | 1.69k | if (src_value != dest_value) |
242 | 1.69k | { |
243 | 1.69k | GType dest_type = G_VALUE_TYPE (dest_value); |
244 | 1.69k | GTypeValueTable *value_table = g_type_value_table_peek (dest_type); |
245 | | |
246 | 1.69k | g_return_if_fail (value_table); |
247 | | |
248 | | /* make sure dest_value's value is free()d */ |
249 | 1.69k | if (value_table->value_free) |
250 | 1.69k | value_table->value_free (dest_value); |
251 | | |
252 | | /* setup and copy */ |
253 | 1.69k | value_meminit (dest_value, dest_type); |
254 | 1.69k | value_table->value_copy (src_value, dest_value); |
255 | 1.69k | } |
256 | 1.69k | } |
257 | | |
258 | | /** |
259 | | * g_value_reset: |
260 | | * @value: An initialized #GValue structure. |
261 | | * |
262 | | * Clears the current value in @value and resets it to the default value |
263 | | * (as if the value had just been initialized). |
264 | | * |
265 | | * Returns: the #GValue structure that has been passed in |
266 | | */ |
267 | | GValue* |
268 | | g_value_reset (GValue *value) |
269 | 3 | { |
270 | 3 | GTypeValueTable *value_table; |
271 | 3 | GType g_type; |
272 | | |
273 | 3 | g_return_val_if_fail (value, NULL); |
274 | 3 | g_type = G_VALUE_TYPE (value); |
275 | | |
276 | 3 | value_table = g_type_value_table_peek (g_type); |
277 | 3 | g_return_val_if_fail (value_table, NULL); |
278 | | |
279 | | /* make sure value's value is free()d */ |
280 | 3 | if (value_table->value_free) |
281 | 3 | value_table->value_free (value); |
282 | | |
283 | | /* setup and init */ |
284 | 3 | value_meminit (value, g_type); |
285 | 3 | value_table->value_init (value); |
286 | | |
287 | 3 | return value; |
288 | 3 | } |
289 | | |
290 | | /** |
291 | | * g_value_unset: |
292 | | * @value: An initialized #GValue structure. |
293 | | * |
294 | | * Clears the current value in @value (if any) and "unsets" the type, |
295 | | * this releases all resources associated with this GValue. An unset |
296 | | * value is the same as an uninitialized (zero-filled) #GValue |
297 | | * structure. |
298 | | */ |
299 | | void |
300 | | g_value_unset (GValue *value) |
301 | 1.69k | { |
302 | 1.69k | GTypeValueTable *value_table; |
303 | | |
304 | 1.69k | if (value->g_type == 0) |
305 | 0 | return; |
306 | | |
307 | 1.69k | g_return_if_fail (value); |
308 | | |
309 | 1.69k | value_table = g_type_value_table_peek (G_VALUE_TYPE (value)); |
310 | 1.69k | g_return_if_fail (value_table); |
311 | | |
312 | 1.69k | if (value_table->value_free) |
313 | 1.69k | value_table->value_free (value); |
314 | 1.69k | memset (value, 0, sizeof (*value)); |
315 | 1.69k | } |
316 | | |
317 | | /** |
318 | | * g_value_fits_pointer: |
319 | | * @value: An initialized #GValue structure. |
320 | | * |
321 | | * Determines if @value will fit inside the size of a pointer value. |
322 | | * This is an internal function introduced mainly for C marshallers. |
323 | | * |
324 | | * Returns: %TRUE if @value will fit inside a pointer value. |
325 | | */ |
326 | | gboolean |
327 | | g_value_fits_pointer (const GValue *value) |
328 | 0 | { |
329 | 0 | GTypeValueTable *value_table; |
330 | |
|
331 | 0 | g_return_val_if_fail (value, FALSE); |
332 | | |
333 | 0 | value_table = g_type_value_table_peek (G_VALUE_TYPE (value)); |
334 | 0 | g_return_val_if_fail (value_table, FALSE); |
335 | | |
336 | 0 | return value_table->value_peek_pointer != NULL; |
337 | 0 | } |
338 | | |
339 | | /** |
340 | | * g_value_peek_pointer: |
341 | | * @value: An initialized #GValue structure |
342 | | * |
343 | | * Returns the value contents as pointer. This function asserts that |
344 | | * g_value_fits_pointer() returned %TRUE for the passed in value. |
345 | | * This is an internal function introduced mainly for C marshallers. |
346 | | * |
347 | | * Returns: (transfer none): the value contents as pointer |
348 | | */ |
349 | | gpointer |
350 | | g_value_peek_pointer (const GValue *value) |
351 | 0 | { |
352 | 0 | GTypeValueTable *value_table; |
353 | |
|
354 | 0 | g_return_val_if_fail (value, NULL); |
355 | | |
356 | 0 | value_table = g_type_value_table_peek (G_VALUE_TYPE (value)); |
357 | 0 | g_return_val_if_fail (value_table, NULL); |
358 | | |
359 | 0 | if (!value_table->value_peek_pointer) |
360 | 0 | { |
361 | 0 | g_return_val_if_fail (g_value_fits_pointer (value) == TRUE, NULL); |
362 | 0 | return NULL; |
363 | 0 | } |
364 | | |
365 | 0 | return value_table->value_peek_pointer (value); |
366 | 0 | } |
367 | | |
368 | | /** |
369 | | * g_value_set_instance: |
370 | | * @value: An initialized #GValue structure. |
371 | | * @instance: (nullable): the instance |
372 | | * |
373 | | * Sets @value from an instantiatable type via the |
374 | | * value_table's collect_value() function. |
375 | | */ |
376 | | void |
377 | | g_value_set_instance (GValue *value, |
378 | | gpointer instance) |
379 | 0 | { |
380 | 0 | GType g_type; |
381 | 0 | GTypeValueTable *value_table; |
382 | 0 | GTypeCValue cvalue; |
383 | 0 | gchar *error_msg; |
384 | |
|
385 | 0 | g_return_if_fail (value); |
386 | 0 | g_type = G_VALUE_TYPE (value); |
387 | 0 | value_table = g_type_value_table_peek (g_type); |
388 | 0 | g_return_if_fail (value_table); |
389 | | |
390 | 0 | if (instance) |
391 | 0 | { |
392 | 0 | g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); |
393 | 0 | g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (instance), G_VALUE_TYPE (value))); |
394 | 0 | } |
395 | | |
396 | 0 | g_return_if_fail (strcmp (value_table->collect_format, "p") == 0); |
397 | | |
398 | 0 | memset (&cvalue, 0, sizeof (cvalue)); |
399 | 0 | cvalue.v_pointer = instance; |
400 | | |
401 | | /* make sure value's value is free()d */ |
402 | 0 | if (value_table->value_free) |
403 | 0 | value_table->value_free (value); |
404 | | |
405 | | /* setup and collect */ |
406 | 0 | value_meminit (value, g_type); |
407 | 0 | error_msg = value_table->collect_value (value, 1, &cvalue, 0); |
408 | 0 | if (error_msg) |
409 | 0 | { |
410 | 0 | g_critical ("%s: %s", G_STRLOC, error_msg); |
411 | 0 | g_free (error_msg); |
412 | | |
413 | | /* we purposely leak the value here, it might not be |
414 | | * in a correct state if an error condition occurred |
415 | | */ |
416 | 0 | value_meminit (value, g_type); |
417 | 0 | value_table->value_init (value); |
418 | 0 | } |
419 | 0 | } |
420 | | |
421 | | /** |
422 | | * g_value_init_from_instance: |
423 | | * @value: An uninitialized #GValue structure. |
424 | | * @instance: (type GObject.TypeInstance): the instance |
425 | | * |
426 | | * Initializes and sets @value from an instantiatable type via the |
427 | | * value_table's collect_value() function. |
428 | | * |
429 | | * Note: The @value will be initialised with the exact type of |
430 | | * @instance. If you wish to set the @value's type to a different GType |
431 | | * (such as a parent class GType), you need to manually call |
432 | | * g_value_init() and g_value_set_instance(). |
433 | | * |
434 | | * Since: 2.42 |
435 | | */ |
436 | | void |
437 | | g_value_init_from_instance (GValue *value, |
438 | | gpointer instance) |
439 | 0 | { |
440 | 0 | g_return_if_fail (value != NULL && G_VALUE_TYPE(value) == 0); |
441 | | |
442 | 0 | if (G_IS_OBJECT (instance)) |
443 | 0 | { |
444 | | /* Fast-path. |
445 | | * If G_IS_OBJECT() succeeds we know: |
446 | | * * that instance is present and valid |
447 | | * * that it is a GObject, and therefore we can directly |
448 | | * use the collect implementation (g_object_ref) */ |
449 | 0 | value_meminit (value, G_TYPE_FROM_INSTANCE (instance)); |
450 | 0 | value->data[0].v_pointer = g_object_ref (instance); |
451 | 0 | } |
452 | 0 | else |
453 | 0 | { |
454 | 0 | GType g_type; |
455 | 0 | GTypeValueTable *value_table; |
456 | 0 | GTypeCValue cvalue; |
457 | 0 | gchar *error_msg; |
458 | |
|
459 | 0 | g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); |
460 | | |
461 | 0 | g_type = G_TYPE_FROM_INSTANCE (instance); |
462 | 0 | value_table = g_type_value_table_peek (g_type); |
463 | 0 | g_return_if_fail (strcmp (value_table->collect_format, "p") == 0); |
464 | | |
465 | 0 | memset (&cvalue, 0, sizeof (cvalue)); |
466 | 0 | cvalue.v_pointer = instance; |
467 | | |
468 | | /* setup and collect */ |
469 | 0 | value_meminit (value, g_type); |
470 | 0 | value_table->value_init (value); |
471 | 0 | error_msg = value_table->collect_value (value, 1, &cvalue, 0); |
472 | 0 | if (error_msg) |
473 | 0 | { |
474 | 0 | g_critical ("%s: %s", G_STRLOC, error_msg); |
475 | 0 | g_free (error_msg); |
476 | | |
477 | | /* we purposely leak the value here, it might not be |
478 | | * in a correct state if an error condition occurred |
479 | | */ |
480 | 0 | value_meminit (value, g_type); |
481 | 0 | value_table->value_init (value); |
482 | 0 | } |
483 | 0 | } |
484 | 0 | } |
485 | | |
486 | | static GType |
487 | | transform_lookup_get_parent_type (GType type) |
488 | 0 | { |
489 | 0 | if (g_type_fundamental (type) == G_TYPE_INTERFACE) |
490 | 0 | return g_type_interface_instantiatable_prerequisite (type); |
491 | | |
492 | 0 | return g_type_parent (type); |
493 | 0 | } |
494 | | |
495 | | static GValueTransform |
496 | | transform_func_lookup (GType src_type, |
497 | | GType dest_type) |
498 | 0 | { |
499 | 0 | TransformEntry entry; |
500 | |
|
501 | 0 | entry.src_type = src_type; |
502 | 0 | do |
503 | 0 | { |
504 | 0 | entry.dest_type = dest_type; |
505 | 0 | do |
506 | 0 | { |
507 | 0 | TransformEntry *e; |
508 | | |
509 | 0 | e = g_bsearch_array_lookup (transform_array, &transform_bconfig, &entry); |
510 | 0 | if (e) |
511 | 0 | { |
512 | | /* need to check that there hasn't been a change in value handling */ |
513 | 0 | if (g_type_value_table_peek (entry.dest_type) == g_type_value_table_peek (dest_type) && |
514 | 0 | g_type_value_table_peek (entry.src_type) == g_type_value_table_peek (src_type)) |
515 | 0 | return e->func; |
516 | 0 | } |
517 | 0 | entry.dest_type = transform_lookup_get_parent_type (entry.dest_type); |
518 | 0 | } |
519 | 0 | while (entry.dest_type); |
520 | | |
521 | 0 | entry.src_type = transform_lookup_get_parent_type (entry.src_type); |
522 | 0 | } |
523 | 0 | while (entry.src_type); |
524 | | |
525 | 0 | return NULL; |
526 | 0 | } |
527 | | |
528 | | static gint |
529 | | transform_entries_cmp (gconstpointer bsearch_node1, |
530 | | gconstpointer bsearch_node2) |
531 | 8.04k | { |
532 | 8.04k | const TransformEntry *e1 = bsearch_node1; |
533 | 8.04k | const TransformEntry *e2 = bsearch_node2; |
534 | 8.04k | gint cmp = G_BSEARCH_ARRAY_CMP (e1->src_type, e2->src_type); |
535 | | |
536 | 8.04k | if (cmp) |
537 | 5.40k | return cmp; |
538 | 2.64k | else |
539 | 2.64k | return G_BSEARCH_ARRAY_CMP (e1->dest_type, e2->dest_type); |
540 | 8.04k | } |
541 | | |
542 | | /** |
543 | | * g_value_register_transform_func: (skip) |
544 | | * @src_type: Source type. |
545 | | * @dest_type: Target type. |
546 | | * @transform_func: a function which transforms values of type @src_type |
547 | | * into value of type @dest_type |
548 | | * |
549 | | * Registers a value transformation function for use in g_value_transform(). |
550 | | * A previously registered transformation function for @src_type and @dest_type |
551 | | * will be replaced. |
552 | | */ |
553 | | void |
554 | | g_value_register_transform_func (GType src_type, |
555 | | GType dest_type, |
556 | | GValueTransform transform_func) |
557 | 680 | { |
558 | 680 | TransformEntry entry; |
559 | | |
560 | | /* these checks won't pass for dynamic types. |
561 | | * g_return_if_fail (G_TYPE_HAS_VALUE_TABLE (src_type)); |
562 | | * g_return_if_fail (G_TYPE_HAS_VALUE_TABLE (dest_type)); |
563 | | */ |
564 | 680 | g_return_if_fail (transform_func != NULL); |
565 | | |
566 | 680 | entry.src_type = src_type; |
567 | 680 | entry.dest_type = dest_type; |
568 | | |
569 | | #if 0 /* let transform function replacement be a valid operation */ |
570 | | if (g_bsearch_array_lookup (transform_array, &transform_bconfig, &entry)) |
571 | | g_warning ("reregistering value transformation function (%p) for '%s' to '%s'", |
572 | | transform_func, |
573 | | g_type_name (src_type), |
574 | | g_type_name (dest_type)); |
575 | | #endif |
576 | | |
577 | 680 | entry.func = transform_func; |
578 | 680 | transform_array = g_bsearch_array_replace (transform_array, &transform_bconfig, &entry); |
579 | 680 | } |
580 | | |
581 | | /** |
582 | | * g_value_type_transformable: |
583 | | * @src_type: Source type. |
584 | | * @dest_type: Target type. |
585 | | * |
586 | | * Check whether g_value_transform() is able to transform values |
587 | | * of type @src_type into values of type @dest_type. Note that for |
588 | | * the types to be transformable, they must be compatible or a |
589 | | * transformation function must be registered. |
590 | | * |
591 | | * Returns: %TRUE if the transformation is possible, %FALSE otherwise. |
592 | | */ |
593 | | gboolean |
594 | | g_value_type_transformable (GType src_type, |
595 | | GType dest_type) |
596 | 0 | { |
597 | 0 | g_return_val_if_fail (src_type, FALSE); |
598 | 0 | g_return_val_if_fail (dest_type, FALSE); |
599 | | |
600 | 0 | return (g_value_type_compatible (src_type, dest_type) || |
601 | 0 | transform_func_lookup (src_type, dest_type) != NULL); |
602 | 0 | } |
603 | | |
604 | | /** |
605 | | * g_value_type_compatible: |
606 | | * @src_type: source type to be copied. |
607 | | * @dest_type: destination type for copying. |
608 | | * |
609 | | * Returns whether a #GValue of type @src_type can be copied into |
610 | | * a #GValue of type @dest_type. |
611 | | * |
612 | | * Returns: %TRUE if g_value_copy() is possible with @src_type and @dest_type. |
613 | | */ |
614 | | gboolean |
615 | | g_value_type_compatible (GType src_type, |
616 | | GType dest_type) |
617 | 10.5k | { |
618 | 10.5k | g_return_val_if_fail (src_type, FALSE); |
619 | 10.5k | g_return_val_if_fail (dest_type, FALSE); |
620 | | |
621 | | /* Fast path */ |
622 | 10.5k | if (src_type == dest_type) |
623 | 10.5k | return TRUE; |
624 | | |
625 | 0 | return (g_type_is_a (src_type, dest_type) && |
626 | 0 | g_type_value_table_peek (dest_type) == g_type_value_table_peek (src_type)); |
627 | 10.5k | } |
628 | | |
629 | | /** |
630 | | * g_value_transform: |
631 | | * @src_value: Source value. |
632 | | * @dest_value: Target value. |
633 | | * |
634 | | * Tries to cast the contents of @src_value into a type appropriate |
635 | | * to store in @dest_value, e.g. to transform a %G_TYPE_INT value |
636 | | * into a %G_TYPE_FLOAT value. Performing transformations between |
637 | | * value types might incur precision lossage. Especially |
638 | | * transformations into strings might reveal seemingly arbitrary |
639 | | * results and shouldn't be relied upon for production code (such |
640 | | * as rcfile value or object property serialization). |
641 | | * |
642 | | * Returns: Whether a transformation rule was found and could be applied. |
643 | | * Upon failing transformations, @dest_value is left untouched. |
644 | | */ |
645 | | gboolean |
646 | | g_value_transform (const GValue *src_value, |
647 | | GValue *dest_value) |
648 | 1.69k | { |
649 | 1.69k | GType dest_type; |
650 | | |
651 | 1.69k | g_return_val_if_fail (src_value, FALSE); |
652 | 1.69k | g_return_val_if_fail (dest_value, FALSE); |
653 | | |
654 | 1.69k | dest_type = G_VALUE_TYPE (dest_value); |
655 | 1.69k | if (g_value_type_compatible (G_VALUE_TYPE (src_value), dest_type)) |
656 | 1.69k | { |
657 | 1.69k | g_value_copy (src_value, dest_value); |
658 | | |
659 | 1.69k | return TRUE; |
660 | 1.69k | } |
661 | 0 | else |
662 | 0 | { |
663 | 0 | GValueTransform transform = transform_func_lookup (G_VALUE_TYPE (src_value), dest_type); |
664 | |
|
665 | 0 | if (transform) |
666 | 0 | { |
667 | 0 | g_value_unset (dest_value); |
668 | | |
669 | | /* setup and transform */ |
670 | 0 | value_meminit (dest_value, dest_type); |
671 | 0 | transform (src_value, dest_value); |
672 | | |
673 | 0 | return TRUE; |
674 | 0 | } |
675 | 0 | } |
676 | 0 | return FALSE; |
677 | 1.69k | } |