Coding practices - Global Variables...

Discussion in 'AutoCAD' started by David Kozina, Oct 26, 2004.

  1. David Kozina

    David Kozina Guest

    OK jefes,

    I understand that typically it's bad juju to have a bunch of *global*
    variables hanging out everywhere in your code.

    At the same time, I find that, with more and more Object Model coding there
    is just tons of this stuff...

    (setq *Acad* (vlax-get-acad-object))

    (setq *ActiveDocument*
    (vlax-get-property
    *Acad*
    'ActiveDocument
    )
    )

    etc.,

    .... and it just keeps getting deeper.

    Now, I've been using *globals* in my routines, and defining them once at
    load time.

    These lines of code will likely never change, assuming I add them to all my
    routines to replace the globals, but I wince at the thought of - what if I
    DO have reason to change it all, someday?

    So - do you prefer globals for this?
    Or just do things the pete and re-pete way?
    Or is there some other way?

    I am leaning towards eliminating these globals, in favor of the in-function
    setqs, but was wondering what your preference is.

    Any thoughts would be appreciated.

    Best regards,
    David Kozina
     
    David Kozina, Oct 26, 2004
    #1
  2. David Kozina

    j.buzbee Guest

    I use only one global, 'jbThisDrawing for the active document. It's set
    when my .mnl file is loaded and then made a protected symbol. Other than
    that I use a few globals for hiding and showing global dialogs but they're
    nilled after the routine exits. The only other Globals are in a few
    routines where it's neccessary to have some values floating around per
    session and those always include a % character. With quite a few routines
    running doing you name it from reactors to ObjectDBX stuff I've never had a
    problem.

    jb
     
    j.buzbee, Oct 26, 2004
    #2
  3. You should be accessing the global data via a procedure. The first time the
    procedure runs it can cache the data in a global variable. The next time it
    runs it simply returns the cached data. This way you aren't accessing, and
    heaven forbid changing, global data in each and every procedure that needs
    the data but going through a single point of control. This way you are free
    to rename the global variable or even change to something other than a
    global variable as needed without having to modify every procedure that
    accesses it.

    double o two inserted
     
    Bobby C. Jones, Oct 26, 2004
    #3
  4. David Kozina

    David Kozina Guest

    Bobby,

    Thanks for your comments.

    Here's an example of what I'm talking about:

    Say in Routines1.lsp I need to access the 'Blocks collection.
    As it turns out, in Routines2.lsp I also need to do this.

    Right now, I have a global variable, *djkBlocks* (and a mess of others),
    created and stored when the drawing is opened (via an .mnl file) that is set
    to:

    (vlax-get-property
    (vlax-get-property
    (vlax-get-acad-object)
    'ActiveDocument
    )
    'Blocks
    )

    So, the code in Routines1.lsp, as well as the code in Routines2.lsp, both
    make use of the global variable *djkBlocks*, rather than creating it from
    scratch each time.

    My question is if this is wise/good, or if it actually IS better to create
    it (localized) from scratch.

    Also, considering the fact that I've got global variables stored I've never
    even used... :-/

    What I've been doing works, but it's lousy for newsgroup Q&As, IYKWIM. :)

    IIRC, I USED to access said objects/properties via a function call that went
    something like this (djkBlocks), which returned the object (as seen in the
    acad help files). Is this what you mean? Or do I misunderstand what you
    mean by 'procedure'?

    Best regards,
    David Kozina
     
    David Kozina, Oct 26, 2004
    #4
  5. David Kozina

    Josh Guest

    Just out of curiousity, how do you protect a symbol? I just perused the
    vlisp help and, as usual, saw the broad concept but not a definitive
    example.

    thx
    Josh
     
    Josh, Oct 27, 2004
    #5
  6. David Kozina

    3ABTPA Guest

    Everything in the lisp is a "glogal" symbol, some of them are changing
    dynamically like vla/vlax- objects, people say they are "pointer" to objects
    but in reality lisp does not interface pointers. All "visible" symbols, from
    the programmer point of view", are about 3000-4000 (this number is my
    observation). It is OK to have any number of your own gloals if you control
    them from "1 command point". I do have ~100-150 and have no problem and my
    vlx project which is about 700kb.
     
    3ABTPA, Oct 27, 2004
    #6
  7. Hey David,
    General coding best practices tells us that code duplication is the root of
    all evil, whether that code is duplicated literally or functionally.
    Remember: duplication = the work of satan and consolidation = beauty in the
    eyes of heaven, so to speak :) So I certainly would not advocate
    duplicating all of that code in every procedure.

    As it turns out global data is not well viewed either. Although I
    personally believe that it falls well short of the vileness of duplication.
    Global data can lead to some hard to track errors because it is open to
    change by any procedure that knows of its existence. And even worse, to
    procedures that you don't have control over. One way to limit the dangers
    of global data is to do just what you are doing and provide a single point
    where the data is set and then it is treated as "read only" in all other
    locations. That is pretty safe if you are the only one that ever writes
    code that will run on your system...or that you can verify that all code
    running on your system that you didn't write doesn't munge your carefully
    placed global data...and that you always remember to only set the data in
    one location...and that whoever takes over your code after your gone
    understands all of these rules...etc..ad naseum...

    In my own coding practices, I always provide access to data via a procedure
    of some sort (this could be a "property" in languages that support them).
    Languages with code modules, either standard or class modules, generally
    allow you to make "global" data hidden from code outside of the module.
    Then that data can only be accessed via procedures that the author creates.
    LISP doesn't have code modules. But you can still hide the fact that you're
    using global data behind a procedure to access it. The data is still
    global, but it's much less susceptible to the pitfalls outlined above if
    it's hidden behind a procedure. And the procedure can even validate the
    data if you're worried that it could become corrupted by someone else's
    code.

    So my advice is to use procedures over duplication and global data.

    Now providing example code in public forums presents a challenge. Do you
    provide a complete working example with code that you normally break out
    into a separate procedure or do you provide an example showing proper coding
    best practices that doesn't function unless you provide all of the support
    procedures that it needs??? When you figure the definitive answer to that
    one, let me know :)

    BTW - I tend to use the generic term 'procedure' for both sub procedures and
    functions. Sorry for the confusion.
     
    Bobby C. Jones, Oct 27, 2004
    #7
  8. I'll have to look for the thread that you mention. When experience of the
    likes of Michael and Tony speak, it pays to listen!

    I have yet to use an IDE that has that type of feature. It looks
    interesting. I guess that as long as all of your code is loaded in the
    current project of the editor, then your ST's work very similarly to using a
    procedure. I'm interested in hearing how it turns out for you. Keep us
    posted.
     
    Bobby C. Jones, Oct 27, 2004
    #8
  9. David Kozina

    David Kozina Guest

    Found the thread:
    http://tinyurl.com/438ed

    More relevant portion starts about halfway through...

    Best regards,
    David Kozina
     
    David Kozina, Oct 27, 2004
    #9
  10. IIRC, awhile back, it seemed that Tony pooh-poohed the (help files) method
    I've been known to contradict myself over time, but in this
    case, if the issue is accessing global symbols directly, then
    what would you say to the fact that all functions whose
    definitions are not nested in the definition of other functions
    are themselves assigned or accessed via a global symbol?

    For example:

    (defun ThisDrawing ()
    (if (not (and (eq (type *thisdrawing*) 'vla-object)
    (not (vla-object-released-p *thisdrawing*))
    )
    )
    (setq *ThisDrawing*
    (vla-get-ActiveDocument
    (vlax-get-acad-object)
    )
    )
    *ThisDrawing*
    )
    )

    Well, if someone else can trash the *ThisDrawing* symbol,
    they can also trash the (ThisDrawing) function. So, is this
    about protection or some other purpose to using functions
    to access global symbols?

    If it is about protection, use a separate namespace VLX,
    and access 'global' symbols directly.

    Sometimes, it makes sense to restrict modifications to a
    global symbol by any means other than a function, and
    that's usually because there may be some common code
    required to produce the value for the global, but that's
    more about avoiding duplication of code in general, and
    not about accessing global symbols via functions.

    PS. Be careful when talking about scope with 'Visual
    Basic Programmers' (that's an oxymoron), because they
    tend to think in concrete rather than abstract terms.

    So, when someone tells you that 'LISP doesn't have code
    modules', they are in fact, mistaken. They're just not called
    code modules in LISP. They're called separate namespace
    VLXs, and they are functionally equivalent to code modules
    in VB.
     
    Tony Tanzillo, Oct 27, 2004
    #10
  11. Regarding that thread.

    I was making the point that a function whose only purpose is to compute and
    return a value, can be replaced with code that iniitializes a symbol to the
    value, and the value can then be referenced via the symbol directly.

    And yes, what I posted in the recent UCS Import thread about registering
    AxDb15.dll squarely contradicts that, because it defines a constant
    function.

    However, there are some cases where you don't want to do unconditional
    initialization because you can't be sure that you will ever need the use the
    result.

    For example, AutoCAD demand-loads its solid modeller DLLs only when they're
    actually needed, and if you do not do anything that requires them, they're
    never loaded. So, there is a benefit to that.

    You can take a similar approach with (vl-load-com), since the first call to
    this function loads Visual LISP's ActiveX libraries, which has significant
    overhead. While the odds are that you are likely to require it, what if the
    user never invokes one of your commands that does require a call to
    (vl-load-com) ? So, by incorporating a demand-driven approach to calling
    it, you could eliminate the need to load it, until its actually required.

    Consider a case where you must load a large amount of data from a file, and
    perhaps store it in a LIST in memory for subsequent use. If there's a remote
    possibility that you may never need to use that data, then it would make
    sense to use a demand-driven approach, where the data is not actually loaded
    until the first time its required.

    That's an example of where a self-refactoring function like the one posted
    in the UCS Import thread can be useful (even if it defines a 'constant
    function'), because it facilitates and simplifies demand-driven
    initialization that is performed only if and when it is absolutely
    necessary.

    So there! <g>



     
    Tony Tanzillo, Oct 27, 2004
    #11
  12. So, when someone tells you that 'LISP doesn't have code
    Good point and very true. In practice they entail quite a bit more work,
    but it is indeed fair to say that they are functionally equivalent.

    On a personal note, no matter what I try to prevent this, our conversations
    always turn out having a slight confrontational feel to them. Have you
    noticed that? I accused my wife last night of turning too many of our
    conversations into confrontations. She quickly retorted that *I* was at
    fault, not her. *I* was at fault!?! Well, after a few minutes of fuming,
    cussing, denial, and finally some introspect, I realized that she had a
    point.

    So if I come across as confrontational to you, I applologize. It must be my
    Dutch bloodline showing. If ever we meet, the first round is on me. As
    long as it's under ten bucks :)
     
    Bobby C. Jones, Oct 27, 2004
    #12
  13. Thanks for the link David!
    --
    Bobby C. Jones

     
    Bobby C. Jones, Oct 27, 2004
    #13
  14. David Kozina

    j.buzbee Guest

    As an innocent by-stander: Bobby it must be you! I thought Tony has been
    acceptionally civil during this "discussion" . . . very out of character ;-)
     
    j.buzbee, Oct 27, 2004
    #14
  15. David Kozina

    Owen Wengerd Guest

    Bobby:

    I don't like duplication of code either, but in many cases
    transportability is also an important factor. In order to make code easily
    transportable, it needs to be self contained with minimal dependence on
    "external" (i.e. global) symbols. David pointed out that he sometimes finds
    that he has trouble posting code here because his code has so much
    dependence on externally defined symbols. IMO, that illustrates the problem.

    In the end, there is no one-size-fits-all approach. As a general rule,
    when I do rely on globals to cache some value or maintain some persistent
    state I at least completely declare and define them in the same file as the
    code that relies on them. :)
     
    Owen Wengerd, Oct 28, 2004
    #15
  16. Well. if we were all in the business of posting code on
    the Autodesk newsgroups, then I suppose that you
    would have a very good point. ;-)

    No one likes rats nest-like interdependence, but that
    is the essence and price of modularity. I've found that
    its benefits outweigh its liabilities. I think modularity
    its even more important for team development since in
    that case, it is not the code duplication per se that
    is the primary problem. Rather, it is the divergence which
    code duplication encourages and usually leads to, that
    often causes very large projects to collapse under their
    own weight.

    However, keeping in mind that library functions are in
    essence 'global' in scope, it seems difficult to promote
    modularity using same, and at the same time, declare
    that variables with similar scope, are bad. How can
    that be?
    That's fine if only one file is dependent on them, but I tend
    to avoid large code files, and instead break up code along
    functional boundaries into several smaller files (as one would
    do in OOP development). That makes it easier for me to work
    on several related pieces of code together, using a tabbed UI.

    I've become heavily addicted to being able to quickly and easily
    switch between two or more related pieces of code in different
    files, by just clicking on a document tab, rather than having
    to constantly scroll a single file, or use navigation controls
    like ClassView (most of which become less effective as the
    size of the project increases). When working in VC++, Delphi,
    or even TextPad with LISP, its not unusual for me to have
    several dozen source files open.

    For those reasons, I usually place global variables that are in
    many cases referenced from multiple source files in a single
    file that's specifically dedicated to global variables. Aside from
    obvious organizational reasons, this also has the advantage of
    ensuring that globals (variables and functions) are defined or
    initialized before any dependent code is loaded.
     
    Tony Tanzillo, Oct 28, 2004
    #16
  17. This is the approach that I have taken with my
    toolbox functions, one function per file. Any
    globals that don't change, like the AutoCAD
    application object, are loaded from one file.

    I started doing it this way just for the pure
    organizational aspect, can't say I dislike it
    one bit. Makes for lots of files, but I hate
    scrolling hundreds of lines of code in one.
     
    Jason Piercey, Oct 28, 2004
    #17
  18. Hi

    I preferred to use local instead of global... i change almost all my programs because of it... but it's better...

    thing to do is to keep those local (or global) variables in the drawing... i did it with Xdata attachment... it's very useful to keep those particular configurations in every drawing.

    luck

    Cristian Leon
    Chile
     
    Cristian Leon, Oct 28, 2004
    #18
  19. Owen!
    I'm glad that you added to the thread. Your voice of reason is profound.
    Indeed there is no one-size-fits-all approach and I admit that I tend to
    forget that :-\

    Looking over my code, I see that I'm all over the place on the issue of
    access to global data/utilities and modularity. I can just about tell you
    what book I was reading prior to writing the code just by looking at it :)
    However, I do see a consistent trend of moving towards a very similar
    style as what Tony describes. Not so much with LISP although I think that's
    due to a lack of experience and as well as a good editor.

    I've asked just about everyone else and now it's your turn. Are you making
    it out to AU this year? I still want to see your demonstration on the
    theory of relativity!
     
    Bobby C. Jones, Oct 28, 2004
    #19
  20. David Kozina

    David Kozina Guest

    Tony,

    OK, this sort of leads to something else I've been wondering about...

    When I start a session of AutoCAD and open a drawing my .mnl file kicks in
    and loads up several other custom routines (.lsp files) - (or otherwise
    readies itself for autoloading files on demand). This is where most of my
    global variables get initialized.

    I've done this for some time (R14? - I think based on suggestions you made
    way back when), and do not even have/make use of acad.lsp nor acaddoc.lsp
    files.

    Now, I open a second drawing - .mnl file kicks in again and does the same
    thing.
    I open a third drawing - etc.

    It all works fine, and seems pretty simple and stable, but I am wondering if
    this is still 'best practice' - or if there are
    better/faster/smarter/simpler ways to do this when a new drawing is opened -
    since they will likely need certain global variables, as well.

    (Note that this thread was initiated due to my thoughts on eliminating
    rarely-used/never-used globals in my code, not to get rid of all of 'em,
    necessarily)

    I've seen in other code you have written functions that makes use of the
    vl-bb 'blackboard' functions to transfer data between "drawing namespaces"
    (not sure if this is the correct terminology), when a new drawing is opened.

    And is this perchance related to the .vlx namespaces you speak of?
    I am completely clueless regarding that, probably 'cuz I don't use VLIDE to
    compile anything.

    Just wondering. Best regards,
    David Kozina
     
    David Kozina, Oct 28, 2004
    #20
Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.