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