📜 ⬆️ ⬇️

Attributes: a look inside

This is a continuation of the article " Introduction to Attributes ." If you are not familiar with the idea of ​​attributes and their syntax - I advise you to start with it. Well, this article discusses how the attributes are arranged from the inside, how to handle them, and what problems may arise.

Guts


When Perl encounters an attribute during compilation, it tries to call one of the handlers of the form MODIFY_SCALAR_ATTRIBUTES , MODIFY_CODE_ATTRIBUTES , etc., for the current class. - depending on the type of data marked with the attribute, like this:

__PACKAGE__->MODIFY_CODE_ATTRIBUTES(\&mySub, 'myAttribute1', 'myAttribute2');

I repeat: these calls are made right at compile time (at the BEGIN stage), or rather, right after perl completes the closing function of the curly brace ( fair UPD from xames : not all handles of the MODIFY_*_ATTRIBUTES are executed at the BEGIN stage. B In particular, MODIFY_SCALAR_ATTRIBUTES executed at the stage of variable initialization ( my $tmp: attr = 0; ) ). Since there can be several attributes, and no one has canceled inheritance, it is considered a good form to go through the list inside the handler, select the attributes that can be processed, and give the superclass to the mercy of what is left.

Similarly, if you want to get a list of function / variable attributes and call the get function from the attributes module for this, the FETCH_SCALAR_ATTRIBUTES FETCH_SCALAR_ATTRIBUTES (or FETCH_CODE_ATTRIBUTES - I think you already understood).
')
Actually, all the features of working with attributes at a low level can be found in perldoc attributes . By the way, for those to whom such an implementation seemed crooked, there is an important remark: " The mechanisms described here are still experimental. Do not rely on the current implementation ". Well, there is nothing more permanent than experimental mechanisms :-)

Recipe for happiness


Obviously, picking attributes with the aforementioned handlers is not a pleasant job. Thank God, the Attribute::Handlers module has appeared in the delivery of the same Perl 5.6, which greatly simplifies writing handlers for attributes and introduces additional interesting features - attributes can have parameters and be processed not only during the BEGIN stage, but also during CHECK, INIT , END. By the way, this module works again with the help of attributes.

So, in order to be able to use the myAttribute attribute to mark functions, it’s enough to write the following code:

use Attribute::Handlers;
sub myAttribute : ATTR(CODE) {
my ($package, $symbol, $referent, $attr, $data, $phase, $filename, $linenum) = @_;
....
}


The handler — the myAttribute method — will be called for the marked function in much the same way as the handlers from the previous examples. There are much more parameters passed to it: in addition to the function reference and the attribute name, there may also be a reference to the symbol table element (GLOB), the phase name BEGIN / CHECK / INIT / END, additional attribute parameters, an indication of the file name and line number. By default, the handler is started during the compilation stage CHECK, when all that can be already loaded and digested.

If we want to pass additional information to the handler, we can do it like this:

sub mySub : myAttribute(p1,p2) {:}

In this case, the handler will receive in the $ data variable an array with p1 and p2 values.

Of the other fun features of Attribute::Handlers , an alternative interface to the tie function is worth noting, which in itself is a rather interesting example of the use of attributes. It makes no sense to dwell on it here, everything is very clear from the documentation.

It should be noted that most of the CPAN modules that use attributes rely on the Attribute::Handlers module. However, it will not always help. Moreover, do not expect that everything is as smooth and beautiful as it seems :)

Rake


Not for nothing is the phase CHECK selected in Attribute::Handlers as default. In this phase, the interpreter has already finished processing the code and placed the functions and everything else in the symbol table, so now there is a technical possibility to get a link to the corresponding GLOB and poshamanit over it - for example, to replace the function. It will not be possible to do this in the BEGIN phase - GLOB is not yet full, and the attribute handler will not be transferred. And here begins the most interesting. If you are developing under mod_perl, all this does not apply to you - you do not have the CHECK phase. According to perldoc perlmod , inside the eval calls and under mod_perl the compilation stages CHECK and INIT do not work, there is only BEGIN and also UNITCHECK. With UNITCHECK, too, not everything is smooth - globally this phase is not intercepted, only at the level of the corresponding module. I did not manage to find a module that would solve this problem and made it possible to get to GLOB using mod_perl as trivially as it can be done in a regular script. At the Attribute::Handlers level, it would be possible to add your own triggers to start handlers and pull them manually — but the module is written so that you can immediately forget about such patches. I was able to solve this problem by actually introducing an additional restriction when writing code - I had to abandon require when loading classes in favor of use . As a result, the work is done in two steps - first, at the BEGIN stage, the list of functions marked with the attribute I need is compiled, and after calling the import method, the main work is done. If someone offers a more human way, I will be glad.

Conclusion


In fact, attributes allow you to bind additional features to familiar program components. The interest of these features is that in fact they are not visible in the code. This is a bit like a tie - you perform elementary actions on the simplest data structures, behind which non-trivial processors are actually hidden. If you need some kind of universal mechanism, but you do not want the features of its implementation to be visible in the code - look in the direction of the attributes.

Source: https://habr.com/ru/post/53455/


All Articles