Page 2 of 2

Posted: Fri Sep 24, 2004 8:36 pm
by tinman
GreenGiant wrote:I think I've missed the point. What would be the advantage of a compiler directive to tell you whether a constant is already defined? Surely you could just run with debug on and it would tell you.
It would tell you at compile time, when the compiler stops giving you a "Constant already defined" error.

One example of where it might be useful is if you use constants in a main source file to control the operation of code in a shared include file. In the include file you can check whether the constant has been defined or not and if not, set a default value or something.

There are probably others too, but I can't think of them offhand.

Posted: Fri Sep 24, 2004 11:52 pm
by GreenGiant
Thanks for trying, but its purpose is still lost on me :) . I see what you mean that it would let you deal with constants that have been declared twice, but I think it'd be easier to physically check for that than write out a CompilerIf for every single constant just in case you'd accidentally already declared it.

Posted: Sat Sep 25, 2004 12:31 am
by tinman
Well, there's also the possibility of using this to do things like e.g. C (although obviously in the following example you would just use XIncludeFile :)

This could be in an included file:

CompilerIf defined(#BLAHBLAHBLAH)=0
#BLAHBLAHBLAH = 69
#foo = 123
#bar = 666
#blah = 987
CompilerEndIf

and in all the sources you need those constants you can include the file, even multiple times and it will not cause problems. I ran into a need for this some time ago, although obviously because I can't remember I must have gotton around it some other way, so not much of a need :)


If there was also a CompilerError directive you could generate your own more meaningful error messages. So instead of "Constant not defined" or "Constant already defined" you could have something explaining why the programmer needs to add it.


You could use it to control which code gets compiled into an executable. For example, if I had 10 procedures, each of which could be included in the executable through the use of a constant, I could define those constants in every single application I use those sets of procedures. However, if I want them all, I could just not define them, and check for that case. This is more useful if you distribute code - people probably won't be bothered setting up constants for every project (I know I wouldn't :). Again, that use would probably be obsolete if PB stripped uncalled procedures.

Posted: Sat Sep 25, 2004 8:25 am
by horst
tinman wrote:You could use it to control which code gets compiled into an executable. For example, if I had 10 procedures, each of which could be included in the executable through the use of a constant, I could define those constants in every single application I use those sets of procedures. However, if I want them all, I could just not define them, and check for that case. This is more useful if you distribute code - people probably won't be bothered setting up constants for every project (I know I wouldn't :).
I remmber a nice feature of my old A86 (DOS) Assembler:

+++ Quote A86 Docs
#IF starts a conditional-assembly block. On the same line,
following the #IF, you provide either a single name, or an
arbitrary expression evaluating to an absolute constant. In this
context, a single name evaluates to TRUE if it is defined and not
equal to the absolute constant zero. A name is FALSE if it is
undefined, or if it has been equated to zero. An expression is
TRUE if nonzero, FALSE if zero.
+++ Unquote

For PureBasic this would simply mean, that a missing constant in "CompilerIf <constant>" would be legal, and the constant assumed zero.

The advantage is that you can have several versions of a function in an Included file, and in the main file you only need set a switch for the function you want (and you don't have to care about the versions that you <b>don't</b> want.
Or you can have a collection of small functions in an Included file this way, where you probably only use a few of them.

There would be no problem if you forget to set a switch, because the compiler would not find the functions that were supposed to be activated.
tinman wrote:Again, that use would probably be obsolete if PB stripped uncalled procedures.
.. and unused arrays and linked lists?

Posted: Sat Sep 25, 2004 12:24 pm
by GreenGiant
You could use it to control which code gets compiled into an executable.
Surely if you want to be able to vary what gets included you could code somethign like this:

Code: Select all

#Type=1

CompilerSelect #Type
  CompilerCase 1
    #Constant1=1
    #Constant2=2
    
    Procedure Proc(a,b)
      Debug a+b
    EndProcedure
    
  CompilerCase 2
    #Constant1=3
    #Constant2=4
    
    Procedure Proc(a,b)
      Debug a+b+1
    EndProcedure
    
CompilerEndSelect

Proc(#Constant1,#Constant2)
Then just specify a number for #Type. So if you wanted to have (for example) different language support you'd put your language procedures in the CompilerSelect section and just compile a new executable for each Case. It'd only include the procedures in its particular case and the constants could be different values too.

Posted: Sat Sep 25, 2004 12:51 pm
by Dare2
My thinking follows GreenGiant on this one.

To write code to test if a constant has been declared, and etc, seems strange when you could just write code to declare the constant, etc. Otherwise it seems pointless - unless a project has got out of hand and nobody knows what is what anymore.

Posted: Sat Sep 25, 2004 1:32 pm
by tinman
Dare2 wrote:My thinking follows GreenGiant on this one.

To write code to test if a constant has been declared, and etc, seems strange when you could just write code to declare the constant, etc. Otherwise it seems pointless - unless a project has got out of hand and nobody knows what is what anymore.
I've seen that happen on relatively small projects where the core functionality has been re-used in many applications. Admittedly this wasn't in PB. OK, three more examples, and then I'm giving up because you've managed to find a way round everything else.

First - there's a constant you can define before you include the Win32 headers called WIN32_LEAN_AND_MEAN, which causes only the core headers to be compiled, speeding up compilation (this is in C, but I guess you could find a similar PB application) and allows you the ability to see when you are trying to use non-core functionality. Do you really want to have to define that constant to zero in every single application just to turn it off? This also applies to an extension of the above example I described where you would have to define every constant just to get the default behaviour.

Second, is the case where you use a whole bunch of includes from different developers. How many times have you seen everybody include their own constants with the same name for the same purpose? #TRUE and #FALSE, until they got put into the PureBasic.res would be one example. Wouldn't it be easier if the include developer actually checked whether their constants were defined and at which value they were defined before re-defining them? OK, you can edit it. You then need to remember to edit the files each time you get an updated version. OK, the compiler will tell you, but it would be much easier if things just worked without mucking around.

Thirdly, most tenious and to be worked around with real macros in PB4 :). What it you have an include file which you use as a macro for e.g. calculations so that it gets included multiple times in your source. Chances are you'd define constants which you used in that file in that file. But no, wait - you'd have to have two files because constants can't be redefined (even to the same value!) or checked as to whether they are defined. More fiddly to work with.

It's not that this would be a show stopping feature, but it would make life slightly easier, and less a pain in the ass. I don't like having to go patching things with comments, or defining useless constants just for the sake of it.

Posted: Sat Sep 25, 2004 3:57 pm
by freak
I second tinman's opinion.
It might not be obvious to everybody, but this helps organizing things, expecially
with big projects, or when reusing code a lot.

However, i would like this to be taken a step further, not just for constants.
My proposal would be a new compiletime command (like SizeOf() or OffsetOf())

Defined(Object [, Object [, Object [, ...]]])

This would simply replaced by a 1 if all given 'objects' are defined, and a 0 if not.
This way, you could use it in CompilerIf and such, and there is no need for
"IfDefined", "IfNotDefined", whatever...

'Object' could then be a Constant, a Structure, Interface, LinkedList, array
or a variable (in the main namespace only (ie Globals, Shareds or variables in the main code)), and in the future, maybe also with macros.


There would be many usefull applications for this. Imagine you have
some IncludeFiles, which all need a LinkedList, but as those files are used
a lot, you can't be sure, which ones are included in the actual projects,
and in which order.
So where do you put the NewList command then?

Well, with the above you could just do this:

Code: Select all

CompilerIf Defined(MyList()) = 0
  Newlist MyList.l()
CompilerEndIf
Put this into every one of those includefiles, and the List will always be there,
no matter what of the files you use. And you won't get any errors.


Now, combined with another possible feature, (that i know tinman is requesting for a while now as well ;))
You can do even more to prevent the common problems when reusing code:

CompilerError "Error Message"

Lets say you have a nice Includefile that you use a lot, but unfortunately, it
requires some Global variables. So what if you give some other variable in
the main code the same name? (happy bug-hunting is all I say ;))
Well, for now we just give those variables a weird name and hope we won't
ever use that same weird name again.

So what could you do with this?

Code: Select all

CompilerIf Defined(MyGlobal1, MyGlobal2, MyGlobal3)
  CompilerError "Global Variable reused!"
CompilerEndIf

Global MyGlobal1, MyGlobal2, MyGlobal3
Voilà, no more trouble!

And of course there is tinman's example with the constants, and many more.


As I said, some of this might not seem too usefull at first, but beleive me,
it helps coding in a way that rules out the sources for some errors right from the start.
I didn't have any use for such features in the past either, but right now i wish they were there.

Lets see if i can talk Fred into this... ;)

Timo

Posted: Sat Sep 25, 2004 7:51 pm
by GreenGiant
Okay then, I'll concede that it could be useful in some circumstances. :P But I still dont think its something I'd ever use. To ensure you had no conflicts with your includes and so on you would have to do a CompilerIf for every single variable and constant before you started using it which to me seems much more work than just coding more sensibly. For example if its a problem to you then just make sure everything is named uniquely. For example if you have an include file which has a parsing command you use in multiple projects then call all your constants things like #Parse_blahblahblah and your variable Parse_blahblahblah.l and little things like that. Thats the way I think I'd go anyway.

Posted: Sat Sep 25, 2004 9:27 pm
by GedB
Here's a simple example of where such a feature would be very useful.

Let imagine that I have written some libraries. To keep executables small I keep shared code in their own libraries.

Consider three of the libraries:
  • Hashes
    Hashtable
    Checksums
Hashtable and Hashes are both dependant upon the Hashes library. By default the standard version of Hashes is xincluded, but what if you want to give the user a choice of using a different algorythm for their hashing?

What you want to do is test to see if a version of Hashes has already been included. If it has, carry on. If it hasn't, load the default version.

So how do the Hashtable or Checksums libraries test to ensure that the Hashes library has already been included?

With an IfDefined directive this is easy. The Hashes library defines the HASHES_INCLUDED constant with a value. Then one of the dependants just tests to see if HASHES_INCLUDED was defined. If HASHES_INCLUDED is undefined then it loads the default version.

You need to test if it was defined because if Hashes hasn't already been included it isn't going to have a value.

Posted: Sat Sep 25, 2004 11:12 pm
by Dare2
Well, given that so many formidable names think it is good, I guess it is good. Maybe, one day, when I grow up to be a programmer I'll understand. :)

Posted: Sat Sep 25, 2004 11:38 pm
by PB
> When I go back and read a post I always worry that my attempts at clarity
> and brevity just come across as impolite and arrogant

I used to worry about that about my posts too, but these days I've given up.
I now just say it like it is, and if someone takes offense, too bad. :twisted: