Accessing values of enumeration types

I am trying to work with the g_enum_get_value() and seems to work, but I am not sure if I am colleting the Information in the right way.

Is this a good approach ?

#include <gtk/gtk.h>

int main ( void )
{
    GtkWidget *button;

    GParamSpec *property;
    const GValue *default_prop_value;

    /// ***
    gtk_init ( NULL, NULL );

    /// ***
    button  = gtk_button_new_with_label ( "Click" );
    g_object_set( button, "relief", GTK_RELIEF_NONE, NULL );

    /// *** Find the Property
    property = g_object_class_find_property ( G_OBJECT_GET_CLASS ( button ), "relief" );

    /// *** Get the name of the Property
    g_print ( "The property name\t=\t%-10s\n", g_param_spec_get_name( property ) );

    /// *** Get the default value of the Property
    default_prop_value = g_param_spec_get_default_value ( property );

    /// ***  Print the Value
    GEnumClass *enum_class;
    GEnumValue *enum_value;
    gint actual_enum_value;
    GType type_default_value;

    /// Get the actual value
    g_object_get ( button, "relief", &actual_enum_value, NULL );

    /// Get the Type of the default value
    type_default_value =  G_VALUE_TYPE ( default_prop_value );

    enum_class = g_type_class_ref ( type_default_value );
    enum_value = g_enum_get_value ( enum_class, actual_enum_value );

    g_print ( "The value is\t\t=\t%s\n", enum_value->value_name );
    g_type_class_unref ( enum_class );

    gtk_main ();
}

Outputs:

The property name	=	relief    
The value is		=	GTK_RELIEF_NONE

Yes, this is pretty much all you need to do to retrieve the enumeration data from the type system.

The GEnumValue structure also holds a short name, called “nick”, which is another method to retrieve the numeric value of the enumeration element.

My goal is to do the same, but without the use of g_object_get() function so that I can print all available properties for the Object.

I found a way to do it and here is the code:

#include <gtk/gtk.h>

int main ( void )
{
    GtkWidget *button;

    GParamSpec *property;
    const GValue *default_prop_value;

    /// ***
    gtk_init ( NULL, NULL );

    /// ***
    button  = gtk_button_new_with_label ( "Click" );
    g_object_set( button, "relief", GTK_RELIEF_NONE, NULL );

    /// *** Find the Property
    property = g_object_class_find_property ( G_OBJECT_GET_CLASS ( button ), "relief" );

    /// *** Get the name of the Property
    g_print ( "The \"%s\" property has the following Values:\n\n", g_param_spec_get_name( property ) );

    /// *** Get the default value of the Property
    default_prop_value = g_param_spec_get_default_value ( property );

    /// ***  Print the Value
    GEnumClass *enum_class;
    GEnumValue *enum_value;
    GType type_default_value;

    /// Get the Type of the default value
    type_default_value =  G_VALUE_TYPE ( default_prop_value );

    enum_class = g_type_class_ref ( type_default_value );


    for (  guint i = 0 ; i < enum_class->n_values ; i++ )
    {
        enum_value = g_enum_get_value ( enum_class, (gint)i );

        g_print ( "\tThe value is = %d | ", enum_value->value );
        g_print ( "The Name  is\t= %s\n", enum_value->value_name );
    }

    g_type_class_unref ( enum_class );

    gtk_main ();
}

And Outputs:

The "relief" property has the following Values:

The value is = 0 | The Name  is	= GTK_RELIEF_NORMAL
The value is = 1 | The Name  is	= GTK_RELIEF_HALF
The value is = 2 | The Name  is	= GTK_RELIEF_NONE

But I am not satisfied, because of this Line:

enum_value = g_enum_get_value ( enum_class, (gint)i );

Here I need to cast “i” to “gint” and I am not happy about this.

or even worst:

for (  gint i = 0 ; i < (gint)enum_class->n_values ; i++ )

here the cast is applied on “enum_class->n_values”.
.
Is there any other way to iterate those values, so that there will be no cast needed ?

No, and you should not use the iterator value as the value itself unless you know exactly what you’re doing.

The values of the enumeration may very well not be in order, so you can’t iterate over them; the GEnumValue.value is the value of the enumeration, not the position in the enumeration.

The entire GEnum/GFlags API is not meant for iteration, but for serialisation and deserialisation; it’s meant to be used when you do have a string and you want to get the integer, or when you have the integer and you want to get the string.

The thing is that the way how I understand the documentation brought me to this program flow and I got the following:

What I understand

  1. the struct GEnumClass looks like this:

     struct GEnumClass {
     GTypeClass  g_type_class;
    
     gint	      minimum;
     gint	      maximum;
     guint	      n_values;
     GEnumValue *values;
    

    };

The documentation says that the variable “guint n_values;” holds the number of possible values, which are 3 ( 0 to 2) based of “enum GtkReliefStyle” for the “relief” property.

The variable “GEnumValue *values;” is a struct which looks like this:

    struct GEnumValue {
    gint	 value;
    const gchar *value_name;
    const gchar *value_nick;
};

Here we have:

gint value;                 // the enum value
const gchar *value_name;    // the name of the value
const gchar *value_nick;    // the nickname of the value

From here I think that, when I get the default value of the “relief” property, I understand that I get the information which is hold in this structure.
The structure holds the information from the PROP_RELIEF implementation:

props[PROP_RELIEF] =
    g_param_spec_enum ("relief",
                       P_("Border relief"),
                       P_("The border relief style"),
                       GTK_TYPE_RELIEF_STYLE,
                       GTK_RELIEF_NORMAL,
                       GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

In other words I undestand that “GEnumValue *values” matches the information from g_param_spec_enum().

In other words:

enum_value->value_name == "GTK_RELIEF_NONE"
enum_value->value_nick == "none"
enum_value->value      == 2 ( which is the last one found in the enumeration ) 

Program flow

If “enum_class->n_values” returns how many possible values are ( 3 in this case), I was thinking that there is no way to do something wrong to use it inside that “loop”.

The enumeration “enum GtkReliefStyle” looks like this:

Indicated the relief to be drawn around a GtkButton.

Members
    GTK_RELIEF_NORMAL       Draw a normal relief.
    GTK_RELIEF_HALF         A half relief. Deprecated in 3.14, does the same as GTK_RELIEF_NORMAL
    GTK_RELIEF_NONE         No relief.

And I see that there are 3 possible values ( ignoring the Deprecated …_HALF now), which it matches the “enum_class->n_values”.

Now that I know all of this, the following loop should be safe:

guint how_many_values = enum_class->n_values;
for (  guint i = 0 ; i < how_many_values ; i++ ) /// we found 3 values
{
    /* get the information from every value */
}

The last thing needed was to get information from every value.

The first  enumeration (index 0) has the value  GTK_RELIEF_NORMAL
The second enumeration (index 1) has the value  GTK_RELIEF_HALF
The third  enumeration (index 2) has the value  GTK_RELIEF_NONE

I still do not understand what I am exactly doing wrong here.

Let’s assume you have an enumeration like:

typedef enum {
  AMAZING_STUFF_FOO = -1,
  AMAZING_STUFF_BAR = 10,
  AMAZING_STUFF_BAZ = 0
} AmazingStuff ;

and the enumeration is registered accordingly:

static const GEnumValue v[] = {
  { AMAZING_STUFF_FOO, "AMAZING_STUFF_FOO", "foo" },
  { AMAZING_STUFF_BAR, "AMAZING_STUFF_BAR", "bar" },
  { AMAZING_STUFF_BAZ, "AMAZING_STUFF_BAZ", "baz" },
  { 0, NULL, NULL },
};

then calling:

  GEnumClass *eclass = g_type_class_ref (awesome_stuff_get_type ());

will get you a GEnumClass that has n_values == 3; but if you call:

  GEnumValue *evalue = g_enum_get_value (eclass, 0);

will get you the GEnumValue for AMAZING_STUFF_BAZ, which is the 3rd value in the enumeration, not the first value in the enumeration.

In other words: g_enum_get_value() returns the GEnumValue for the value, not the GEnumValue at the given position.

If you want the GEnumValue at a given index, you could do something like:

for (guint idx = 0; idx < eclass->n_values; i++)
  {
    const GEnumValue *evalue = eclass->values[i];

    const char *enum_name = evalue->value_name;
    const char *enum_nick = evalue->value_nick;
    int enum_value = evalue->value;

    // ...
  }

Or, in other words, you should access the GEnumClass directly, and not use the API.

Quick edit to add: This approach is normally safe, because projects generally autogenerate the GType data for enumeration types, but nothing stops somebody from putting GEnumValue structures in any order they want. In theory there is no way to know the original order of the C enumeration from the GType data.

1 Like

Moved this sub-thread to a different topic, as it doesn’t match the original topic any more.

1 Like

Yes, Yes, this is what I was looking for.

Now I understand what I have to do.

Thank you Sir.

[this is redundant, I didn’t see @ebassi’s answer in my inbox after the thread has been moved]

The last thing needed was to get information from every value.

The first enumeration (index 0) has the value GTK_RELIEF_NORMAL The
second enumeration (index 1) has the value GTK_RELIEF_HALF The third
enumeration (index 2) has the value GTK_RELIEF_NONE |

The thing @ebassi is trying to tell you is that indicies in
GEnumClass::values do NOT have to match their actual values (their
GEnumValue::value). Often it is indeed the case, but that’s mostly
coincidental.

I still do not understand what I am exactly doing wrong here.

You should not call g_enum_get_value() in the loop. g_enum_get_value()
can transform a GEnumValue::value into the corresponding GEnumValue;
that is g_enum_get_value(enum_class, enume_value.value) == enum_value

GEnumClass::values is an array, so you should index this instead
of trying to call g_enum_get_value() to do that – which it doesn’t do.

The loop you want is something like this:

for (guint i = 0; i < enum_class->n_values; i++) {
	const GEnumValue *enum_value = &enum_class->values[i];
	// and NOT g_enum_get_value (enum_class, (gint)i);

	// ...
}

This said, for serialization purposes @ebassi is right that you really
don’t have to loop over the values, just use g_enum_get_value*() to get
the corresponding GEnumValue from their value, name or nick.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.