Author Topic: Function Declaration  (Read 502 times)

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Function Declaration
« on: March 09, 2014, 04:16:17 pm »


               

I just encountered something in NESS that I do not understand - a function declaration without an include.


 


I am currently looking at spawn_cfg_camp


 


At the top of the script are no includes. The script also lacks a main as it is included in other scripts.


 


spawn_cfg_camp however does have several function declarations without function definitions, the the functions are defined elsewhere - and this works.


 


I did not know that you could do this in NWN.


 


Anyone know more about this and want to elucidate?


 


What I think is happening:


a bunch of scripts are all included at the same time.


one script (in the include chain) defines and declares many reusable functions


other scripts (at the end/top of the include chain) declare the functions when needed, and thus do not need to include anything


               
               

               
            

Legacy_Proleric

  • Hero Member
  • *****
  • Posts: 1750
  • Karma: +0/-0
Function Declaration
« Reply #1 on: March 09, 2014, 11:39:21 pm »


               What you suspect makes sense.


The compiler first replaces #include directives with their contents. If what results is a legal script, it will compile, period.
               
               

               
            

Legacy__Guile

  • Hero Member
  • *****
  • Posts: 1308
  • Karma: +0/-0
Function Declaration
« Reply #2 on: March 15, 2014, 09:17:11 am »


               

It will be perfectly fine, but rather confusing, and poor coding standards to most of us nwn coders...


 


Essentially, it's indicative to making a normal script (where the functions are above the main), and the script sees them BEFORE the main script....


 


The include is read before the main, that's why they are put above the main & above other functions...


 


You don't have to declare a function unless you are putting it below the main, otherwise it's only declared for readability, unless it's in an include.


 


In an include a function is declared with comments as a coding standard, so that when you go to search for the function to the right while scripting, you will see the function creators notes about the function.  (Best Coding Practices)


 


Hope that added to the clarity of the answer to your questions.



               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Function Declaration
« Reply #3 on: March 15, 2014, 11:47:22 pm »


               

It's not poor coding practice, generally speaking, though I haven't looked at NESS. There are sometimes reasons for more unusual orderings of code, including occasionally saving on resource count (or just convenience).


 


Consider, for example, our nonstandard setup for persistent area of effect scripts:


Persistent AoE Include


 


There you have an example of declarations without implementations. You also, more unusually, have a void main in the include.


 


What's being done there is that the void main uses those various declared functions in its body - but uses varying implementations of those functions, which are found in the spellscripts including this include. This allows all our persistent aoes to run through the same setup, while remaining highly modular.


 


Note that this is an older version of the code I'm posting. Updated versions of Skywing's compiler require #pragma declarations to make this work.


 


Funky



               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Function Declaration
« Reply #4 on: March 17, 2014, 05:52:50 pm »


               

Funky, thanks for the word. I am a bit both confused and intrigued by your example of including a main in an include. I do not understand the compiler trickery you are pulling off here. Is the main from your include being pulled into the spell script and thus compiling differently because you have different function definitions in the spell script?


 


What boggles my mind here is that I thought that the script with the main was the one that compiled. But what you are saying is that the top of the chain - the script that is not included by any others, but has the first include, is the one which compiles. Is my description close to what is happening? If so… wow, that opens up all kinds of ways to structure your code that I never considered before.


               
               

               
            

Legacy_meaglyn

  • Hero Member
  • *****
  • Posts: 1451
  • Karma: +0/-0
Function Declaration
« Reply #5 on: March 17, 2014, 06:56:59 pm »


               

What's compiled is the main() (or startingConditional...) routine.   As long as you only have one such in the namespace of the file you are compiling then it gets compiled into an executable script.  If you don't have main or startingconditional then nothing is compiled. But the compiler needs to read the file (and any include files and any include files in those files...) before it can know if there's a main or not.


 


There is nothing per se that indicates a given file is an "include" file or not. 


 


@Funky: what happens when building the module gets to that include script of yours though? It will try to compile it and since there are no bodies for all those routines which are supposed to be defined by the including script it will


fail. Do you limit your compilation to specific scripts or is that what skywings compile somehow helps out with?


               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Function Declaration
« Reply #6 on: March 18, 2014, 04:40:40 am »


               

That's basically it, yes. And yes, the script with the main IS the one that compiles. The main is in ALL the scripts that include it, so they all compile. The trick here is that, the declarations are above the main, at the top of the script (in the include), while the implementations are in each individual script including that main, below it. This allows the main to run those functions, but that main will compile differently for each script that includes it, using the implementations in each specific script.


 


Here are some examples, using a more current version of the aoeper include:


ac_aoeper.inc


nw_s0_acidfog


nw_s0_cloudkill


nw_s0_darkness


nw_s0_grease


nw_s0_inccloud


nw_s0_mindfog


 


LMK if you have any questions.


 


Funky



               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Function Declaration
« Reply #7 on: March 18, 2014, 04:52:24 am »


               


What's compiled is the main() (or startingConditional...) routine.   As long as you only have one such in the namespace of the file you are compiling then it gets compiled into an executable script.  If you don't have main or startingconditional then nothing is compiled. But the compiler needs to read the file (and any include files and any include files in those files...) before it can know if there's a main or not.


 


There is nothing per se that indicates a given file is an "include" file or not. 


 


@Funky: what happens when building the module gets to that include script of yours though? It will try to compile it and since there are no bodies for all those routines which are supposed to be defined by the including script it will


fail. Do you limit your compilation to specific scripts or is that what skywings compile somehow helps out with?




Building the module? Heh. We haven't been able to do that since...2007, I think? If you use NWNTX you can use Skywings compiler in the toolset. We have no need to build the entire module each time we update, though we do compile all scripts before upping a build to our live servers, just as good practice. Aside from the last time we added custom factions, which was a couple years back, all we do is compile, really. Using the default compiler, our module would crash half a thousand different ways at least. We use nonstandard for loops, which it doesn't allow (think for(oItem = GetFirstItemInInventory(oPC);GetIsObjectValid(oItem);GetNextItemInInventory(oPC)) {//dostuff} ), #pragmas (the examples above), thousands more identifiers than it allows...the list goes on and on. Long story short: use Skywings compiler. Being able to compile in prefixed batches (think all scripts beginning with fky_chat, for example) is a huge advantage in its own right. It also catches all kinds of coding errors the vanilla compiler doesn't, which can create stack over- and under-flows and all sorts of zanyness. Skywing is the man. Use his compiler. Use.. it... @.@


 


Hell, we can't even use the erf'er anymore. Copy out of temp0, is how we do exports. Though we were doing that since well before we couldn't use the erf'er, anyway, so I suppose it doesn't matter.


 


As far as your specific question, about lacking 'bodies', look at the code I posted. Each of those persisten aoe scripts compiles its own ncs, because each has its own group of functions with a void main(). The subfunctions in that void main differ from script to script, is all. Think of how the scripts would look if the include was inserted at the top of each. Like so:


////


function declarations


 


void main, using those functions


 


function implementations


////


The declarations and void main are in the include;the implementations are in the individual aoe scripts.


 


Funky



               
               

               
            

Legacy__Guile

  • Hero Member
  • *****
  • Posts: 1308
  • Karma: +0/-0
Function Declaration
« Reply #8 on: March 18, 2014, 06:23:58 am »


               

I believe where people are getting confused is, Funky uses NWNTX to compile scripts, which allows him to do things which are "not normal" in the toolset....


 


If you tried to compile a script in the normal Toolset with a main in the "main script" and a main in the "include" you would indeed get an error.... (see below)


 


ERROR: DUPLICATE FUNCTION IMPLEMENTATION (main)



               
               

               
            

Legacy_Proleric

  • Hero Member
  • *****
  • Posts: 1750
  • Karma: +0/-0
Function Declaration
« Reply #9 on: March 18, 2014, 08:50:16 am »


               

I believe where people are getting confused is, Funky uses NWNTX to compile scripts, which allows him to do things which are "not normal" in the toolset....
 
If you tried to compile a script in the normal Toolset with a main in the "main script" and a main in the "include" you would indeed get an error.... (see below)
 
ERROR: DUPLICATE FUNCTION IMPLEMENTATION (main)

What Funky is saying works in the vanilla toolset, too (I just tried it). In the script source files, the only "main" is in the include. The compiler expands the includes prior to syntax checking, so by the time it gets around to compiling, it has introduced one (and only one) main to each script.
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Function Declaration
« Reply #10 on: March 18, 2014, 12:03:51 pm »


               

OK. So a bit of semantics has us describing the same thing in different ways, but yeah the trick is the ordering of the include chain. It is not where main() is located that is critical during the compile, but the script from which the include chain begins. Very interesting. I wish I understood this years ago.


 


In addition the ability to put declarations wherever you need them - while not as advanced of FKY's slight of main() trickery - should enable me to reduce a few of my subsystems by one script. My chains used to go like this. Main/Executable --->  Functions ---> User Configurable --> Globals. Instead I should be able to do Main/Executable --> User Config --> Functions/Globals   as long as I include function declarations in User Config and provide a note that this should not be deleted. Not a huge saving in resources, but its nice to keep things tighter.


 


Some day my code will be as clean and as tight as Higher Ground's stuff. '<img'>



               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Function Declaration
« Reply #11 on: March 18, 2014, 12:14:12 pm »


               


OK. So a bit of semantics has us describing the same thing in different ways, but yeah the trick is the ordering of the include chain. It is not where main() is located that is critical during the compile, but the script from which the include chain begins. Very interesting. I wish I understood this years ago.




I still cant see what is it good for. The Funky's example particularry is seriously complicated this way while it could be very simple. I just cant see an advantage in this method. Can someone explain it to me?


 


I mean, what can you accomplish with this method, that wouldnt be otherwise possible via standard way of writing spells?



               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Function Declaration
« Reply #12 on: March 18, 2014, 12:23:41 pm »


               

The way I see it, you only have to write the non-configurable stuff once, ShaDoOoW.  Many spells are structured similarly but have different data inserted into them. Nevertheless you've got nearly identical mains in each of them. If you wanted to change the way the main behaved you'd have to change it in every similar spell script. The way Higher Ground does it, you just tweak the one main in that include and every spell in that area of effect suite is updated at the same time.


 


The way I am doing things now I either tediously edit each main, or I pull all the scripts out of temp0, and run find|sed on them for a batched find/replace. Usually I just edit each one one at a time.



               
               

               
            

Legacy_meaglyn

  • Hero Member
  • *****
  • Posts: 1451
  • Karma: +0/-0
Function Declaration
« Reply #13 on: March 18, 2014, 01:26:14 pm »


               


As far as your specific question, about lacking 'bodies', look at the code I posted. Each of those persisten aoe scripts compiles its own ncs, because each has its own group of functions with a void main(). The subfunctions in that void main differ from script to script, is all.


 




 


I meant when that aoe include with the main was compiled itself. Since it does not implement the functions which are prototyped it will fail to compile. As long you either don't bother to compile it, or don't care if you get an error, it works. I understand how the scripts including it work. It was more a process question.


 


If there was a linux version which would allow me to point to a directory of include files instead of requiring the bifs I might revisit Skywing's compiler.  I  suppose I should look into getting it to replace the original one in the toolset. I have not tried nwntx since I switched to using wine for the toolset...


               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Function Declaration
« Reply #14 on: March 18, 2014, 01:46:15 pm »


               


The way I see it, you only have to write the non-configurable stuff once, ShaDoOoW.  Many spells are structured similarly but have different data inserted into them. Nevertheless you've got nearly identical mains in each of them. If you wanted to change the way the main behaved you'd have to change it in every similar spell script. The way Higher Ground does it, you just tweak the one main in that include and every spell in that area of effect suite is updated at the same time.


 


The way I am doing things now I either tediously edit each main, or I pull all the scripts out of temp0, and run find|sed on them for a batched find/replace. Usually I just edit each one one at a time.




Ok i see. But, you can accomplish this exact advantage without this setup by using a same spellscript for multiple spells and switch-case differences.