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 | { |
141 | transform_array = g_bsearch_array_create (bconfig: &transform_bconfig); |
142 | } |
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 | { |
148 | value->g_type = value_type; |
149 | memset (s: value->data, c: 0, n: sizeof (value->data)); |
150 | } |
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 | { |
165 | GTypeValueTable *value_table; |
166 | /* g_return_val_if_fail (G_TYPE_IS_VALUE (g_type), NULL); be more elaborate below */ |
167 | 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 | value_table = g_type_value_table_peek (type: g_type); |
171 | |
172 | if (value_table && G_VALUE_TYPE (value) == 0) |
173 | { |
174 | /* setup and init */ |
175 | value_meminit (value, value_type: g_type); |
176 | value_table->value_init (value); |
177 | } |
178 | else if (G_VALUE_TYPE (value)) |
179 | g_warning ("%s: cannot initialize GValue with type '%s', the value has already been initialized as '%s'" , |
180 | G_STRLOC, |
181 | g_type_name (g_type), |
182 | g_type_name (G_VALUE_TYPE (value))); |
183 | else /* !G_TYPE_IS_VALUE (g_type) */ |
184 | g_warning ("%s: cannot initialize GValue with type '%s', %s" , |
185 | G_STRLOC, |
186 | g_type_name (g_type), |
187 | value_table ? "this type is abstract with regards to GValue use, use a more specific (derived) type" : "this type has no GTypeValueTable implementation" ); |
188 | return value; |
189 | } |
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 | { |
202 | g_return_if_fail (src_value); |
203 | g_return_if_fail (dest_value); |
204 | g_return_if_fail (g_value_type_compatible (G_VALUE_TYPE (src_value), G_VALUE_TYPE (dest_value))); |
205 | |
206 | if (src_value != dest_value) |
207 | { |
208 | GType dest_type = G_VALUE_TYPE (dest_value); |
209 | GTypeValueTable *value_table = g_type_value_table_peek (type: dest_type); |
210 | |
211 | g_return_if_fail (value_table); |
212 | |
213 | /* make sure dest_value's value is free()d */ |
214 | if (value_table->value_free) |
215 | value_table->value_free (dest_value); |
216 | |
217 | /* setup and copy */ |
218 | value_meminit (value: dest_value, value_type: dest_type); |
219 | value_table->value_copy (src_value, dest_value); |
220 | } |
221 | } |
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 | { |
235 | GTypeValueTable *value_table; |
236 | GType g_type; |
237 | |
238 | g_return_val_if_fail (value, NULL); |
239 | g_type = G_VALUE_TYPE (value); |
240 | |
241 | value_table = g_type_value_table_peek (type: g_type); |
242 | g_return_val_if_fail (value_table, NULL); |
243 | |
244 | /* make sure value's value is free()d */ |
245 | if (value_table->value_free) |
246 | value_table->value_free (value); |
247 | |
248 | /* setup and init */ |
249 | value_meminit (value, value_type: g_type); |
250 | value_table->value_init (value); |
251 | |
252 | return value; |
253 | } |
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 | { |
267 | GTypeValueTable *value_table; |
268 | |
269 | if (value->g_type == 0) |
270 | return; |
271 | |
272 | g_return_if_fail (value); |
273 | |
274 | value_table = g_type_value_table_peek (G_VALUE_TYPE (value)); |
275 | g_return_if_fail (value_table); |
276 | |
277 | if (value_table->value_free) |
278 | value_table->value_free (value); |
279 | memset (s: value, c: 0, n: sizeof (*value)); |
280 | } |
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 | { |
294 | GTypeValueTable *value_table; |
295 | |
296 | g_return_val_if_fail (value, FALSE); |
297 | |
298 | value_table = g_type_value_table_peek (G_VALUE_TYPE (value)); |
299 | g_return_val_if_fail (value_table, FALSE); |
300 | |
301 | return value_table->value_peek_pointer != NULL; |
302 | } |
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 | { |
317 | GTypeValueTable *value_table; |
318 | |
319 | g_return_val_if_fail (value, NULL); |
320 | |
321 | value_table = g_type_value_table_peek (G_VALUE_TYPE (value)); |
322 | g_return_val_if_fail (value_table, NULL); |
323 | |
324 | if (!value_table->value_peek_pointer) |
325 | { |
326 | g_return_val_if_fail (g_value_fits_pointer (value) == TRUE, NULL); |
327 | return NULL; |
328 | } |
329 | |
330 | return value_table->value_peek_pointer (value); |
331 | } |
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 | { |
345 | GType g_type; |
346 | GTypeValueTable *value_table; |
347 | GTypeCValue cvalue; |
348 | gchar *error_msg; |
349 | |
350 | g_return_if_fail (value); |
351 | g_type = G_VALUE_TYPE (value); |
352 | value_table = g_type_value_table_peek (type: g_type); |
353 | g_return_if_fail (value_table); |
354 | |
355 | if (instance) |
356 | { |
357 | g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); |
358 | g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (instance), G_VALUE_TYPE (value))); |
359 | } |
360 | |
361 | g_return_if_fail (strcmp (value_table->collect_format, "p" ) == 0); |
362 | |
363 | memset (s: &cvalue, c: 0, n: sizeof (cvalue)); |
364 | cvalue.v_pointer = instance; |
365 | |
366 | /* make sure value's value is free()d */ |
367 | if (value_table->value_free) |
368 | value_table->value_free (value); |
369 | |
370 | /* setup and collect */ |
371 | value_meminit (value, value_type: g_type); |
372 | error_msg = value_table->collect_value (value, 1, &cvalue, 0); |
373 | if (error_msg) |
374 | { |
375 | g_warning ("%s: %s" , G_STRLOC, error_msg); |
376 | g_free (mem: 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 | value_meminit (value, value_type: g_type); |
382 | value_table->value_init (value); |
383 | } |
384 | } |
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 | { |
405 | g_return_if_fail (value != NULL && G_VALUE_TYPE(value) == 0); |
406 | |
407 | if (G_IS_OBJECT (instance)) |
408 | { |
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 | value_meminit (value, G_TYPE_FROM_INSTANCE (instance)); |
415 | value->data[0].v_pointer = g_object_ref (instance); |
416 | } |
417 | else |
418 | { |
419 | GType g_type; |
420 | GTypeValueTable *value_table; |
421 | GTypeCValue cvalue; |
422 | gchar *error_msg; |
423 | |
424 | g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); |
425 | |
426 | g_type = G_TYPE_FROM_INSTANCE (instance); |
427 | value_table = g_type_value_table_peek (type: g_type); |
428 | g_return_if_fail (strcmp (value_table->collect_format, "p" ) == 0); |
429 | |
430 | memset (s: &cvalue, c: 0, n: sizeof (cvalue)); |
431 | cvalue.v_pointer = instance; |
432 | |
433 | /* setup and collect */ |
434 | value_meminit (value, value_type: g_type); |
435 | value_table->value_init (value); |
436 | error_msg = value_table->collect_value (value, 1, &cvalue, 0); |
437 | if (error_msg) |
438 | { |
439 | g_warning ("%s: %s" , G_STRLOC, error_msg); |
440 | g_free (mem: 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 | value_meminit (value, value_type: g_type); |
446 | value_table->value_init (value); |
447 | } |
448 | } |
449 | } |
450 | |
451 | static GType |
452 | transform_lookup_get_parent_type (GType type) |
453 | { |
454 | if (g_type_fundamental (type_id: type) == G_TYPE_INTERFACE) |
455 | return g_type_interface_instantiatable_prerequisite (interface_type: type); |
456 | |
457 | return g_type_parent (type); |
458 | } |
459 | |
460 | static GValueTransform |
461 | transform_func_lookup (GType src_type, |
462 | GType dest_type) |
463 | { |
464 | TransformEntry entry; |
465 | |
466 | entry.src_type = src_type; |
467 | do |
468 | { |
469 | entry.dest_type = dest_type; |
470 | do |
471 | { |
472 | TransformEntry *e; |
473 | |
474 | e = g_bsearch_array_lookup (transform_array, &transform_bconfig, &entry); |
475 | if (e) |
476 | { |
477 | /* need to check that there hasn't been a change in value handling */ |
478 | if (g_type_value_table_peek (type: entry.dest_type) == g_type_value_table_peek (type: dest_type) && |
479 | g_type_value_table_peek (type: entry.src_type) == g_type_value_table_peek (type: src_type)) |
480 | return e->func; |
481 | } |
482 | entry.dest_type = transform_lookup_get_parent_type (type: entry.dest_type); |
483 | } |
484 | while (entry.dest_type); |
485 | |
486 | entry.src_type = transform_lookup_get_parent_type (type: entry.src_type); |
487 | } |
488 | while (entry.src_type); |
489 | |
490 | return NULL; |
491 | } |
492 | |
493 | static gint |
494 | transform_entries_cmp (gconstpointer bsearch_node1, |
495 | gconstpointer bsearch_node2) |
496 | { |
497 | const TransformEntry *e1 = bsearch_node1; |
498 | const TransformEntry *e2 = bsearch_node2; |
499 | gint cmp = G_BSEARCH_ARRAY_CMP (e1->src_type, e2->src_type); |
500 | |
501 | if (cmp) |
502 | return cmp; |
503 | else |
504 | return G_BSEARCH_ARRAY_CMP (e1->dest_type, e2->dest_type); |
505 | } |
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 | { |
523 | 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 | g_return_if_fail (transform_func != NULL); |
530 | |
531 | entry.src_type = src_type; |
532 | 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 | entry.func = transform_func; |
543 | transform_array = g_bsearch_array_replace (barray: transform_array, bconfig: &transform_bconfig, key_node: &entry); |
544 | } |
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 | { |
562 | g_return_val_if_fail (src_type, FALSE); |
563 | g_return_val_if_fail (dest_type, FALSE); |
564 | |
565 | return (g_value_type_compatible (src_type, dest_type) || |
566 | transform_func_lookup (src_type, dest_type) != NULL); |
567 | } |
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 | { |
583 | g_return_val_if_fail (src_type, FALSE); |
584 | g_return_val_if_fail (dest_type, FALSE); |
585 | |
586 | /* Fast path */ |
587 | if (src_type == dest_type) |
588 | return TRUE; |
589 | |
590 | return (g_type_is_a (type: src_type, is_a_type: dest_type) && |
591 | g_type_value_table_peek (type: dest_type) == g_type_value_table_peek (type: src_type)); |
592 | } |
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 | { |
614 | GType dest_type; |
615 | |
616 | g_return_val_if_fail (src_value, FALSE); |
617 | g_return_val_if_fail (dest_value, FALSE); |
618 | |
619 | dest_type = G_VALUE_TYPE (dest_value); |
620 | if (g_value_type_compatible (G_VALUE_TYPE (src_value), dest_type)) |
621 | { |
622 | g_value_copy (src_value, dest_value); |
623 | |
624 | return TRUE; |
625 | } |
626 | else |
627 | { |
628 | GValueTransform transform = transform_func_lookup (G_VALUE_TYPE (src_value), dest_type); |
629 | |
630 | if (transform) |
631 | { |
632 | g_value_unset (value: dest_value); |
633 | |
634 | /* setup and transform */ |
635 | value_meminit (value: dest_value, value_type: dest_type); |
636 | transform (src_value, dest_value); |
637 | |
638 | return TRUE; |
639 | } |
640 | } |
641 | return FALSE; |
642 | } |
643 | |