Jump to content

Any thoughts on using Eval on Enums with IniFiles?


 Share

Recommended Posts

I’ve got quite used to using Enums for constant values, either in a standalone fashion, or as a way to index heterogeneous arrays.

They provide a great deal of flexibility and readability to the code.  However I also use INI files quite a bit, and often for values that also represented by Enums.

However, inside the ini files I typically store such values as a “naked” integer, which undercuts a lot of the value of the Enums in the first place.  To improve on this, I am considering handling ini files and enums like this from now on:

Local Enum $off, $low, $high
Local Enum $tiny, $small, $big

$loglevel=Eval(IniRead("cf.ini","General", "LOGLEVEL", "low"))
$guisize=Eval(IniRead("cf.ini","General", "GUISIZE", "tiny"))

the cf.ini:

[General]
LOGLEVEL=high
GUISIZE=big

Question, are there any strange effects or any real downside to this method?

Code hard, but don’t hard code...

Link to comment
Share on other sites

6 minutes ago, JockoDundee said:

To improve on this, I am considering handling ini files and enums like this from now on

I too have a great appreciation for enums, especially when they add to the overall readability and maintainability of the code.  However, I wouldn't consider the ini example you gave as adding to either the readability or maintainability.  Of course it's just my opinion, but it seems to add levels of complexity and obfuscation that are totally unnecessary.  If you are the only one that needs to maintain the scripts, then all of the complexity and obfuscation really doesn't matter.  But if anyone else has to pick up your code and try to maintain it...well let it suffice to say that I'm glad it wouldn't be me.   ;)

Link to comment
Share on other sites

1 minute ago, TheXman said:

I too have a great appreciation for enums, especially when they add to the overall readability and maintainability of the code.  However, I wouldn't consider the ini example you gave as adding to either the readability or maintainability.  Of course it's just my opinion, but it seems to add levels of complexity and obfuscation that are totally unnecessary.  If you are the only one that needs to maintain the scripts, then all of the complexity and obfuscation really doesn't matter.  But if anyone else has to pick up your code and try to maintain it...well let it suffice to say that I'm glad it wouldn't be me.   ;)

So here’s how it helps me, as a solo coder:

1) I’m editing the InI file, I see it says STATUS=4.  
Then I need to go look at the Enum definition and finger count to see that 4=$running.  And then find the number for the STATUS, 3=$standby that I want before changing it to 3.

2) I want to insert a STATUS in between $standby and $running.  If I forget that the value of STATUS is stored in the iniFile, I’m screwed.  With eval, it still works.

 

Code hard, but don’t hard code...

Link to comment
Share on other sites

1 hour ago, JockoDundee said:

So here’s how it helps me, as a solo coder

I totally understood the reasoning behind it from the start.  Like I said, I use enums a lot too -- as you can see in my UDFs.

I guess what it really boiled down to, for me, is that I'm just not a big fan of the Eval function and I try to avoid it unless absolutely necessary.  If you have to use it to reference and de-reference your ini values, it just seems to add unnecessary complexity and obfuscation.  If it were me, I would store the integer values in the ini file and use the enums for the setting and testing of the ini values in the script.  Storing the values as enums, for my taste, is a bit much.  But it is just one old guys opinion.  :)

As for the reasoning as to how it helps you as a solo coder, if you prefer to quickly be able to identify string-based status values (running, stopped, paused. etc.) in your ini file, because you frequently need to read it manually, then why don't store them that way and let the script handle how they are set and tested?  That way you can't forget what the values are and can add new values as needed and without having to worry about existing values.  :)  In other words, your example my be a better fit for constants instead of enums.  If you are storing the values as strings anyway, you might as well store them as they need to be and not have to de-reference them with Eval functions and thereby remove the unnecessary complexity and obfuscation.  ;)

Edited by TheXman
Link to comment
Share on other sites

1 hour ago, TheXman said:

if you prefer to quickly be able to identify string-based status values (running, stopped, paused. etc.) in your ini file, because you frequently need to read it manually, then why don't store them that way and let the script handle how they are set and tested?  That way you can't forget what the values are and can add new values as needed and without having to worry about existing values.

this is what I started to do, store Status as “RUNNING”, “PAUSED”, etc. And it does solve the problems I stated above.  
However, it has two disadvantages over Eval(Enum)  that I see:

1)  It loses the information stored in the Enum itself - for instance if I want to cycle += thru the statuses or compare > one Status to another.  Not all my iniFile values are like this but some definitely are.

2) There is error checking built in with Eval. String values that don’t match variable names set @error.  For arbitrary strings however, I would have to write a function to check against some list of strings myself.

Considering this, and considering that the visual difference between String constants and Eval(Enums) is just the word Eval around the function, it seemed appealing.

In any case, I appreciate your point of view, and thank you for making me think through in more detail my thoughts! 

 

Code hard, but don’t hard code...

Link to comment
Share on other sites

They do totally different things.  One returns the value of a variable.  The other returns the value of an expression.

I guess a single variable name, by itself, is an expression, which sort of makes them the same in that way.  But, the string passed to an Eval needs to resolve to a variable name and that's it.  The value passed to the Execute function resolves to an expression.

I don't use either of them so I'm not an expert on them.  However, I think you usually see the Eval functions in relation to Assign functions.

Const $VARNAME = "iValue"

Assign($VARNAME, 1)

ConsoleWrite('$VARNAME                      = ' & $VARNAME & @CRLF)
ConsoleWrite('Eval($VARNAME)                = ' & Eval($VARNAME) & @CRLF)
ConsoleWrite('Eval($VARNAME) + 1            = ' & Eval($VARNAME) + 1 & @CRLF)
ConsoleWrite('Execute("$iValue + 1")        = ' & Execute("$iValue + 1") & @CRLF)
ConsoleWrite('Execute("Eval($VARNAME) + 1") = ' & Execute("Eval($VARNAME) + 1") & @CRLF)


Output:

$VARNAME                      = iValue
Eval($VARNAME)                = 1
Eval($VARNAME) + 1            = 2
Execute("$iValue + 1")        = 2
Execute("Eval($VARNAME) + 1") = 2

Just putting those simple examples together made my head hurt.  I can't stand Eval or Execute, especially Eval, and avoid them like the plague.  I'd rather use additional code than use either of them, unless ABSOLUTELY necessary.  :)

Edited by TheXman
Better example
Link to comment
Share on other sites

10 hours ago, TheXman said:

They do totally different things. 

Perhaps they do.  Nonetheless, my question was carefully constructed as 

10 hours ago, JockoDundee said:

what can Eval do that Execute can’t?

And I think the answer to that is almost nothing:

$VARNAME = "Value"

If Eval("VARNAME") = Execute("$VARNAME") Then MsgBox(1, "", $VARNAME)

Eval doesn’t want the $, so that’s a plus.  Also, 3 less characters in the command:)

Code hard, but don’t hard code...

Link to comment
Share on other sites

So after playing around with it a bit, I decided to go all in on the dynamic approach:

the code -

#pragma compile(Console,True)

Local Enum $off, $low, $high
Local Enum $tiny, $small, $big

Local $keyValue=IniReadSection("cf.ini", "General")

For $n=1 To $keyValue[0][0]
   Assign($keyValue[$n][0], Execute($keyValue[$n][1]),2)
Next

ConsoleWrite("Values - LOGLEVEL: "& $loglevel &"  GUISIZE: "& $guisize & @CRLF)

the sample .ini -

[General]
loglevel=$high
guisize=$tiny

the output -

Values - LOGLEVEL: 2  GUISIZE: 0

Basically replaces all the assignment statements you normally have to do when reading in ini files - as well as allowing Enums or other Consts to be used as descriptive values.

For me it’s definitely a time saver.  @xman may disagree :)

Code hard, but don’t hard code...

Link to comment
Share on other sites

Link to comment
Share on other sites

1 hour ago, Nine said:

I like your approach, although I would use Eval instead of Execute and remove all $ signs in the ini file.  Would make it more elegant IMO. 

The only thing is with Eval, since there are no $ signs, you can’t assign literals easily, where as execute can do either.

Otherwise I totally agree with you.

 

Code hard, but don’t hard code...

Link to comment
Share on other sites

37 minutes ago, JockoDundee said:

The only thing is with Eval, since there are no $ signs, you can’t assign literals easily

Not sure I am following you there.  Do you have an example of what you find hard to do ?

Link to comment
Share on other sites

2 hours ago, Nine said:

Not sure I am following you there.  Do you have an example of what you find hard to do ?

So, sometimes I would like to set a variable to an Enum constant, like in the example given above where I set the variable $loglevel to the Enum value of $high.  Which in the example is 2.

Because Assign is expecting a variable name, it doesn’t need or want the leading $, so therefore, i just use loglevel in the file.

And if I had used Eval instead of Execute, it also would have expected the $ to be omitted. And therefore I would have just used high, as you pointed out.  This is all good as far as it goes.

However, if I don’t have a constant like $high to assign to $loglevel, and just want assign a literal like 7, then the Eval approach fails because it will look for the variable $7 even though I just have a 7 in the file.

Execute, on the other hand, allows either the use of a constant like $high, or a literal like 7 to be the assignment, because it is capable of evaluating complex expressions using variables, operators and literals.

for a concrete example, if the code was written with Eval instead of Execute, and you had this file, it would have a problem with the 7.

[General]
loglevel=7
guisize=tiny

Does that make sense?

Code hard, but don’t hard code...

Link to comment
Share on other sites

1 hour ago, JockoDundee said:

Does that make sense?

Yep, but you could do this :

Local Enum $off, $low, $high
Local Enum $tiny, $small, $big

Local $keyValue=IniReadSection("cf.ini", "General")

For $n=1 To $keyValue[0][0]
   Assign($keyValue[$n][0], IsDeclared($keyValue[$n][1]) ? Eval($keyValue[$n][1]) : $keyValue[$n][1], 2)
Next

ConsoleWrite("Values - LOGLEVEL: " & $loglevel & "  GUISIZE: " & $guisize & "  test: " & $Test & @CRLF)

with 

[General]
loglevel=high
guisize=small
test=7

You could replace isDeclared by StringIsDigit if you prefer...or any other test more appropriate

Edited by Nine
Link to comment
Share on other sites

Great work, Nine!

So I put it in but I ran into a small problem - some of my variables on the right side are of the style $array[$enum].   Whichever way I tried it I couldn’t get Eval to Eval it properly.

Using your ternaristical construction as inspiration I amended this:

Assign($keyValue[$n][0], IsDeclared($keyValue[$n][1]) ? Eval($keyValue[$n][1]) : $keyValue[$n][1],2)

to this:

Assign($keyValue[$n][0], IsDeclared($keyValue[$n][1]) ? Eval($keyValue[$n][1]) : Execute($keyValue[$n][1]) ? Execute($keyValue[$n][1]) : $keyValue[$n][1],2)

this seems to handle it all, literals, variables with $ and without, expressions arrays etc.

thx for your input so far :)

Edited by JockoDundee
missed a $

Code hard, but don’t hard code...

Link to comment
Share on other sites

Link to comment
Share on other sites

I avoid using Eval and Execute much like TheXman. Recently though, I was trying to do something similar to what you're describing now. I create a vendor array with various properties that I use scattered throughout my script. I used an array along with my Enum to supply the names for the config file... something like this:

Global Enum $VENDOR_NAME, $VENDOR_TIN, $VENDOR_ADDRESS, $VENDOR_ID, $VENDOR_TYPE, $VENDOR_SIZE
Global Const $aVENDOR_DESC[$VENDOR_SIZE] = ["Name", "Tax ID", "Address", "ID", "Type"]

Func LoadVendor($sName)
    
    Local $aVendor[$VENDOR_SIZE]
    For $i=0 To $VENDOR_SIZE - 1
        $aVendor[$i] = IniRead($sConfig, $sName, $aVENDOR_DESC[$i], "")
    Next
    Return $aVendor
    
EndFunc

This was really helpful when I was building my script because I was making it up as I went along. I didn't know what other Vendor fields I might need, but I could add them as needed. Also, invalid lines in the config file were ignored.

This also makes both your code and config file look "clean" (imho :))

All my code provided is Public Domain... but it may not work. ;) Use it, change it, break it, whatever you want.

Spoiler

My Humble Contributions:
Personal Function Documentation - A personal HelpFile for your functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager
InputBoxes - Simple Input boxes for various variable types

Link to comment
Share on other sites

1 hour ago, seadoggie01 said:

This also makes both your code and config file look "clean" (imho :))

Yes, nicely done.  
The only thing I don’t love is that the descriptions and the Enums can get out of sync, if you’re not careful.

Fwiw, you might find this interesting:

 

Code hard, but don’t hard code...

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...