After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 543189 - static constructor not called when class is accessed via static methods
static constructor not called when class is accessed via static methods
Status: RESOLVED OBSOLETE
Product: vala
Classification: Core
Component: Objects
unspecified
Other All
: Normal enhancement
: 1.2
Assigned To: Vala maintainers
Vala maintainers
: 543190 565852 577149 621944 686337 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2008-07-15 22:02 UTC by Stef Walter
Modified: 2018-05-22 13:08 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
codegen: always init class if it has a static constructor when using static methods (3.56 KB, patch)
2011-03-14 12:50 UTC, Marco Trevisan (Treviño)
none Details | Review

Description Stef Walter 2008-07-15 22:02:52 UTC
The static construct block of a class is executed before a new object of that type is created. However I would expect it to be executed when static methods are called.

Example:

/* 
 * valac test.vala
 */

using GLib;

public class Statico : GLib.Object {
	public static int test_value;

	static construct {
		test_value = 5;
	}

	public static int get_test_value() {
		return test_value;
	}
}


public class Test : GLib.Object {
	public static void main(string[] args) {
		/* Should print '5', but prints '0' */
		stdout.printf("%d\n", Statico.get_test_value());
	}
}
Comment 1 Jared Moore 2008-07-16 02:21:43 UTC
*** Bug 543190 has been marked as a duplicate of this bug. ***
Comment 2 Jürg Billeter 2008-07-30 21:37:45 UTC
Confirming.
Comment 3 Jürg Billeter 2009-01-09 23:26:05 UTC
*** Bug 565852 has been marked as a duplicate of this bug. ***
Comment 4 Luca Bruno 2010-03-10 20:23:34 UTC
static construct is relative to the class initialization, shouldn't it be "class construct" then the method should be a class method? In that case it has sense to have the class initialized.

Anyway, keeping the static thing, all static methods have to call g_type_class_ref/unref in order to create the class then initialize it. It is somewhat forced to have "static construct" depend on class initialization. What about having a good ondemand system for initializing static values and calling static construct? I'd suggest a static construct that doesn't depend on class_init and a static boolean to check whether it's initialized or not. Then for each property/method prelude call this initialization. This call will go also in the class_init.
Comment 5 Jürg Billeter 2010-03-14 19:36:58 UTC
Both, static construct and class construct, exist. static construct is never called more than once, however, class construct is called for every subclass that is being initialized.

With your proposal, public static fields would still not always be initialized as the init function can't be called from the outside.
Comment 6 Jürg Billeter 2010-03-26 17:04:11 UTC
*** Bug 577149 has been marked as a duplicate of this bug. ***
Comment 7 Saggi Mizrahi 2010-06-19 10:01:49 UTC
*** Bug 621944 has been marked as a duplicate of this bug. ***
Comment 8 Tristan Brindle 2010-09-17 11:28:21 UTC
You can work around this by doing

g_type_class_ref(MY_TYPE);

in C, or

typeof(MyType).class_ref();

in Vala. The class_ref() function tells the GObject run-time to initialise the
class if it hasn't already done so.

Perhaps it would be worth the Vala compiler adding a g_type_class_ref/unref at the start/end of every static method? Vala already does a lot of refcounting at function entry/exit, so it would be unlikely to make any difference performance-wise.
Comment 9 Marco Trevisan (Treviño) 2011-03-14 09:53:11 UTC
Maybe ref/unref in each static method isn't needed... It should be done just once.

I'd do something like this (when a static construct is set):

static void foo_static_construct(void) {
	if (g_type_class_peek(TYPE_FOO) == NULL) {
		g_type_class_unref(g_type_class_ref(TYPE_FOO));
	}
}

void foo_method1 (void) {
foo_static_construct();
// ...
}

void foo_method2 (void) {
foo_static_construct();
// ...
}

This will cause the _init function to be called just once and the ref/unref procedure is done only on first static method call.
Comment 10 Marco Trevisan (Treviño) 2011-03-14 12:50:54 UTC
Created attachment 183335 [details] [review]
codegen: always init class if it has a static constructor when using static methods

This patch does what I've explained above...

Basically a new "static init" function is defined as:

	static void foo_static_init (void) {
		if (g_type_class_peek (TYPE_FOO) == NULL) {
			g_type_class_unref (g_type_class_ref (TYPE_FOO));
		}
	}

So when a static constructor si defined, each static method is generated as:

	void foo_method (void) {
		// variable initializers
		foo_static_init ();
		// function body
	}

Even if it's a workaround, I guess that this is the only possible way to get properly working static constructors with the current GType implementation.
Comment 11 Colomban Wendling 2011-06-09 17:32:48 UTC
Not sure if you consider it to be the exact same problem (though the solution/workaround would be almost the same), but the problem also exists when having static members that are implicitly dynamically set:

public class Test : Object {
  private static int[] array = {
    1, 2, 3
  };

  public static void func ()
  {
    stdout.printf ("%d\n", array.length);
  }
}

public static void main ()
{
  /* should print 3, but prints 0 */
  Test.func();
}


This is because the array (and its size) is created and set in class_init(), which is obviously not called before the static method. Here too, guarding the static method with a "simple" class_ref() would do the trick.

Actually, this applies to any public static method or class member, e.g. anything that has a chance to be accessed before the class get instantiated.
Comment 12 geert jordaens 2011-08-09 18:36:52 UTC
Calling the already generate foo_get_type() function would be less work.

    static void foo_method (void) {
        // variable initializers
        foo_get_type ();
        // function body
    }
Comment 13 Tristan Brindle 2011-09-14 15:46:26 UTC
(In reply to comment #12)
> Calling the already generate foo_get_type() function would be less work.
> 
>     static void foo_method (void) {
>         // variable initializers
>         foo_get_type ();
>         // function body
>     }

This wouldn't work: get_type() doesn't cause the class to be initialised. You either need to create an instance, or call g_type_class_peek().
Comment 14 Tristan Brindle 2011-09-14 15:51:13 UTC
It's worth noting that instantiation of static members is a tricky thing in C++; in that case, constructors are called before main(), and the order in which it happens is more-or-less random (or rather, "implementation defined").

Does anyone know how it's done in C# and Java?
Comment 15 bruce 2012-06-30 20:24:21 UTC
in java, if we try to call Klass.staticMethod, the class loader will load Klass first, and any static class members will be defined and initialised, and any static code block will be executed.
the static class member initialization/code block execution follows FIFO rules. 

for example:
class Test {
    private static String aStr= "1";
    static {
        aStr = "2";
    }
    public static void func(){}
}
if we call Test.func(), first aStr is defined and initialised with value "1", and then aStr is assigned with value "2"
Comment 16 Jürg Billeter 2012-10-19 19:11:48 UTC
*** Bug 686337 has been marked as a duplicate of this bug. ***
Comment 17 Tristan Brindle 2013-04-30 03:56:58 UTC
I still think the best approach for this is to generate a g_type_class_ref()/unref() pair at the start and end of each static method to demand-create the class if no instance has yet been created.

This would have the same effect as bruce describes in Comment 15 -- it would execute the static construct block if it hasn't been run yet.

It still wouldn't solve the problem of accessing static *fields*, but we could get around that by forbidding public static fields, making the use of an accessor function mandatory.

For example:

class OtherClass : Object {}

class MyClass : Object
{
    private static OtherClass a_class; // May not be public

    static construct {
        a_class = new OtherClass();
    }

    public static get_other_class_member() {
        // Generated C code for this method calls g_type_class_ref() here,
        // which runs the static construct block if necessary
        return a_class;
        // Generated g_type_class_unref() here
    }
}

Other than the minuscule performance impact of the extra ref/unref, can anyone think of a downside to this approach?
Comment 18 Luca Bruno 2013-04-30 07:21:37 UTC
(In reply to comment #17)
> I still think the best approach for this is to generate a
> g_type_class_ref()/unref() pair at the start and end of each static method to
> demand-create the class if no instance has yet been created.
> 
> This would have the same effect as bruce describes in Comment 15 -- it would
> execute the static construct block if it hasn't been run yet.
> 
> It still wouldn't solve the problem of accessing static *fields*, but we could
> get around that by forbidding public static fields, making the use of an
> accessor function mandatory.

You can't make this mandatory for BC reasons.

Rather than doing the ref/unref inside the static method, I'd rather do it when accessing static stuff: i.e. add the boilerplate to the caller.

Anyway I'm not fond with the idea to fix this particular issue. Due to the nature of C and gobject, I feel this will be more painful in the future than what it is currently.
Comment 19 Daniel Espinosa 2017-02-17 18:01:52 UTC
Is important Vala users to understand better C and GObject, in order to select a possible solution.

This is an enhancement, because Vala task is to help not to solve all issues. Vala performs well for common approaches. This is more advanced GObject use case and may be specific for not general cases.

There should be a point where Vala should be put aside and write pure C class in order to get more control on construction/destruction of your classes. For general use cases, Vala cover almost all of them.
Comment 20 Daniel Espinosa 2017-02-17 18:03:01 UTC
Moved to unspecified version. Because it is a problem for all of them.
Comment 21 GNOME Infrastructure Team 2018-05-22 13:08:34 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to GNOME's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/vala/issues/11.