AtomicPorkchop
AtomicPorkchop

Reputation: 2675

How I reference hash inside of subroutine?

I am trying to use the tie function of the module Config::IniFiles but I cannot figure out how to reference the hash inside of sub routine. If I remove the tie function and related code from the sub routine it works perfectly.

This is the line I thought would work, but tells me that "$cfg" is not initialized.

use Config::IniFiles
sub config_file {
    my $cfg_file = 'settings.ini';
    my %cfg;
    tie %cfg, 'Config::IniFiles', ( -file => "$cfg_file" );

    #my $cfg = Config::IniFiles->new( -file => $cfg_file );

}

sub esx_host_check {
    my $esx_host = config_file()->$cfg{ESX}{host};
}

I am sure it is something simple, but I am stumped.

Upvotes: 2

Views: 351

Answers (3)

Eric Strom
Eric Strom

Reputation: 40142

First off, the tie function returns the internal hidden object that represents the tie, and not the tied variable itself. Secondly, you can not return a plural tied value (hash or array) from a subroutine and have it work the way you are expecting. You need to return a reference to the plural value, and then dereference it when you need to use it.

use Config::IniFiles;

sub config_file {
    tie my %cfg, 'Config::IniFiles', -file => 'settings.ini';  # tie variable
    return \%cfg;  # return a reference to the tied variable
}

sub esx_host_check {
    my $esx_host = config_file()->{ESX}{host}; # call sub and dereference value
}

If you are going to use the config hash more than a few times, its probably best to build it and then cache the result:

{my $cfg;
sub config_file {
    tie %$cfg, 'Config::IniFiles', -file => 'settings.ini' unless $cfg;
    return $cfg;
}}

This is a little different than above. First, we setup config_file to be a closure around the private variable $cfg. Note that it is a scalar and not a hash. Then in the sub, we check to see if the variable has been initialized, and if not, call tie. tie is passed a first argument of %$cfg which dereferences the undefined value as a hash, which has the effect of storing the tied hash reference into $cfg.

While a little more complicated, this technique will only need to build the config hash once, potentially saving a lot of time.

Upvotes: 7

Linus Kleen
Linus Kleen

Reputation: 34632

(1) always start your perl code with use strict. You should have received a warning in esx_host_check() about an unknown %cfg

(2) use use vars(...) to implement "global" identifiers:

use vars qw(%cfg);

sub one
    {
        tie %cfg, ....
    }

sub two
    {
        my $value = $cfg{foo}{bar};
    }

Upvotes: 1

Hugmeir
Hugmeir

Reputation: 1259

You are declaring %cfg with my (good!), so it's only visible inside the config_file sub; You then tie it, which returns the underlying Config::IniFiles object, and, as it is the last entry of the function, it returns that object... So I'm not sure why you are tie'ing in the first place, rather than just using the commented line.

In any case, config_file() returns a Config::IniFiles object. You then try to call a method, named by the contents of the variable $cfg{ESX}{host}.. A variable that doesn't exist!

If you want to use the tie interface, add a return \%cfg; to the end of config_file. If you want to use the object interface.. Well, I can only point you to the docs.

Upvotes: 0

Related Questions