Cycling74 / min-api

High-level C++-language application programming interface for Max
MIT License
55 stars 23 forks source link

Attributes call their setter methods before object is fully constructed #60

Open pixsperdavid opened 8 years ago

pixsperdavid commented 8 years ago

My test case for this involves the attribute disable function, but this is indicative of a wider problem when accessing any class member, which could cause a lot of pain for users.

    attribute<bool> disable_simple_integer { this, "disable_simple_integer", true,
        setter { MIN_FUNCTION {
            simple_integer.disable(args[0]);
            return { args[0] };
        }}
    };

    attribute<long> simple_integer { this, "simple_integer", 100};

The above code-block causes an error on object construction, because 'simple_integer' is not yet constructed when the setter function is called to set the default value of 'disable_simple_integer'. Obviously it's possible to put checks for this in, but this could end up with a lot of extra boilerplate in setter methods.

Additionally, these kind of errors can be quite hard to spot for inexperienced developers, and also can be introduced from working code merely by re-ordering members in the class definition.

Is it possible to delay the initialisation of attributes with their default value (via their setters) until after object construction?

tap commented 8 years ago

Yes, I agree that the order of initialization is very important and it is possible to easily wander into problems like this. I have done so myself.

I have spent a fair amount of time looking at and prototyping different "solutions" but, in fact, I have found the gotchas of the various alternatives to be more grievous than the situation displayed here -- which can be addressed by awareness of initialization ordering. This certainly needs some documentation, however.

As an example, one solution that I prototyped queued all of the initial attribute values when they were constructed. Then the queue was drained during object construction and those values assigned. The result of this is even more problematic because now the memory of attribute data is completely uninitialized and the some of the same ordering problems appear (when attributes are dependent on the values of other attributes) but are more difficult to isolate and debug and could even crash.

One more thought I have about this is that I wonder if a problem like this (initialization dependencies) could be caught using static analysis. Perhaps we can define some grammars for Xcode's static analyzer to look for this pattern somehow?

pixsperdavid commented 8 years ago

Understood. I've always found 'ordering of class members affects execution' situations to be kind of icky, but then I spend most of my coding time in C# where this is far less of a issue than C++. I'll think about the best way to document this.

Static analysis is a good idea. Additionally, maybe we need an 'is_inititalized' function on the attribute class to be able to test this easily?

tap commented 8 years ago

is_inititalized() is a good idea.

The object itself has an inititalized() method, so the two should follow the same naming convention... though I don't have a clear idea on which naming convention is better or more universal or more rational.

tap commented 8 years ago

I look into this earlier today. There is no bullet-proof way to implement this is_initialized() check on attributes. The point at which such a method would return false the object hasn't yet been allocated and contains random data.

When some sort of delicate sequencing is needed, however, we have a good example in the xfade~ and panner~ objects.